diff --git a/.gitignore b/.gitignore
index 450b9404dd50a9050b9dad42d6f4363ae13699d9..a30b4b373ec285d52d494ebb90c11bda588c4f3e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,34 +1,21 @@
 nbproject
+/config/sshkeys/*
+/config/projects/*
 /config/config_custom.php
-/config/sshkeys/git@gitlab.iml.unibe.ch.pub
-/config/sshkeys/git@gitlab.iml.unibe.ch
-/config/projects/mmmu-sf2.json
-/config/projects/mmmu-sf2.json.ok
-/config/projects/doccom.json
-/config/projects/imlplayer.json_1.ok
-/config/projects/mmmu-sf2.json_1.ok
-/config/projects/ci.json_1.ok
-/config/projects/doccom.json_1.ok
-/config/projects/ci.json.ok
-/config/projects/imlplayer.json
-/config/projects/imlplayer.json.ok
-/config/projects/doccom.json.ok
-/config/projects/ci.json
+/config/inc_user2projects.php
+/data/imldeployment/packages/*
+/data/.ssh/known_hosts
 /data/imldeployment/data/database/logs.db
+/data/imldeployment/data/database/logs2.db
 /data/imldeployment/data/projects/*.json*
+/data/imldeployment/data/sshkeys/*
+
 /public_html/deployment/dummy.db
-/public_html/deployment/classes/ramdb.class.php
 /public_html/~cache/
-/public_html/deployment/classes/spooler-handler.class.php
-/public_html/deployment/pages/act_jstest.php
-/public_html/deployment/classes/html-adminltetest.tpl.php
-/public_html/deployment/adminlte/
-/config/inc_user2projects.php
-/shellscripts/spooler/
+/shellscripts/
 /public_html/valuestore/data/versioncache.db
 /public_html/vendor/medoo/
 /config/_inc_projects_config.php
 /config/inc_projects_config.php
 /config/inc_user2roles.php
 /.vscode/
-data/imldeployment/packages/*
\ No newline at end of file
diff --git a/config/config_custom.php.dist b/config/config_custom.php.dist
index b8ea8520b2b2a29e848145447f904319e5d89a3b..50c09d5c8769a35144225b26528d2078b8234068 100644
--- a/config/config_custom.php.dist
+++ b/config/config_custom.php.dist
@@ -91,6 +91,7 @@ return [
 
         'rollout'=>[
             'default'=>[],
+            /*
             'ssh'=>[
                 'user'=>'imldeployment',
                 'privatekey'=>'',
@@ -99,13 +100,13 @@ return [
                 'command'=>'/usr/local/bin/puppetrun.sh',
             ],
             'awx'=>[
-                'url'=>'https://awx.sys.iml.unibe.ch/api/v2', // no ending "/"
-                'user'=>'api-ci',
-                'password'=>'awRSbdB2rkViaBXBKOvtr11DEoZJSqHceih1hEE4awrjIO1wuArKu85WmetsRp63',
+                'url'=>'https://awx.example.com/api/v2', // no ending "/"
+                'user'=>'ciserver',
+                'password'=>'ciserver',
                 'jobtemplate'=>'36',
                 'tags'=>'rollout',
-                // 'ignore-ssl-error'=>false,
             ],
+            */
         ],
     ],
     
@@ -128,6 +129,42 @@ return [
             ],
         ],
     ],
+
+    // ----------------------------------------------------------------------    
+    // APPMONITOR
+    // see <https://os-docs.iml.unibe.ch/appmonitor/>
+    // ----------------------------------------------------------------------    
+
+    "appmonitor" => [
+
+        // notification
+
+        // email notification for this application
+        /*
+        "email" => [
+            "sysadmin@example.com"            
+        ],
+        */
+        // email notification for this application
+        /*
+        "slack" => [
+            ["#sysadmins", "https://hooks.slack.com/services/AAA/BBB/CCC" ]
+        ],
+        */
+
+        // limit access to appmonitor client to specific IP addresses
+        /*
+        "ip" => [
+            '127.0.0.1',
+            '::1',
+            '10.0.2.2',
+        ],
+        */
+
+        // limit access to appmonitor client to specific token
+        // "token" => ["token", "1234567890"],
+
+
     // ----------------------------------------------------------------------    
 
 ];
\ No newline at end of file
diff --git a/config/inc_projects_config.php b/config/inc_projects_config.php
index 0345ab7affa26af284cbbff71d9e6cc83aca0136..17cfa94e3e59fee88a94805e1f903765c5ff42ef 100644
--- a/config/inc_projects_config.php
+++ b/config/inc_projects_config.php
@@ -1,4 +1,10 @@
 <?php
+/**
+ * Array of global configuration for CI server
+ * @var array
+ */
+$aConfig = [];
+global $aConfig;
 
 $aConfig = include('config_defaults.php');
 if (file_exists(__DIR__.'/config_custom.php')){
diff --git a/config/lang/de-de.json b/config/lang/de-de.json
index e22cebcd08e8e444b7e55313a1445b485b531360..87765b902bc095e6d38e3c44d3c93987e6220407 100644
--- a/config/lang/de-de.json
+++ b/config/lang/de-de.json
@@ -27,8 +27,6 @@
     "overview-hint-dblclick":"Doppelklick um das Projekt [%s] zu &ouml;ffnen",
     "overview-filter":"Filter",
     "overview-filter-hint":"nur dieses Projekt anzeigen",
-    "overview-simpleview":"zur einfachen Ansicht",
-    "overview-extview":"zur erweiterten Ansicht",
     "overview-textsearch":"Freitext-Filter (Regex)",
     "overview-textsearch-hint":"gesucht wird in Projektnamen, Beschreibungen, Commit-Messages",
     "overview-filterprj":"Projekt-Filter",
@@ -179,6 +177,8 @@
     "page-build-reload-branches": "Branches neu laden",
     "page-build-branch-on-target": "Auf der Ziel-Phase ist Branch [%s] installiert.",
     "page-build-switch-to-target": "Setze [%s]",
+    "page-build-warning-version-exists": "Im der Queue f&uuml;r Phase [%s] ist die Version %s bereits vorhanden!",
+    "page-build-warning-version-is-installed": "Im der Phase [%s] ist die Version %s bereits vorhanden!",
     
     "page-cleanup-info-archives-deleted": "Folgende Verzeichnisse wurden gel&ouml;scht",
     "page-cleanup-info-archives-left": "Folgende Versionen sind derzeit vorhanden - mit Angabe, wo sie verwendet werden",
diff --git a/config/lang/en-en.json b/config/lang/en-en.json
index 3c86ce2727e0ffb820c13447544b1176a806c93a..a909472aa54a3fcdbcb4a2adf107d6975c4cb14c 100644
--- a/config/lang/en-en.json
+++ b/config/lang/en-en.json
@@ -26,8 +26,6 @@
     "overview-hint-dblclick":"Double click to open the project [%s]",
     "overview-filter":"Filter",
     "overview-filter-hint":"Show only this project",
-    "overview-simpleview":"Show simple view",
-    "overview-extview":"Show extended view",
     "overview-textsearch":"Text filter (regex)",
     "overview-textsearch-hint":"searches in project names, descripions, commit messages",
     "overview-filterprj":"Project filter",
@@ -180,6 +178,8 @@
     "page-build-branch-on-target": "On target phase the branch [%s] was installed.",
     "page-build-switch-to-target": "Set [%s]",
     "page-build-reload-branches": "Reload all branches",
+    "page-build-warning-version-exists": "In the queue of phase [%s] the version %s already exists!",
+    "page-build-warning-version-is-installed": "In the phase [%s] the version %s already exists!",
     
     "page-cleanup-info-archives-deleted": "The following directories have been deleted",
     "page-cleanup-info-archives-left": "This versions currently exist - with information of their usage",
diff --git a/docker/.env b/docker/.env
index edab665ef52ae19c007f68f55b3744c7be96f2bf..b7d2a5e3ac966f2c7f52f1d13a621c3db9fc4907 100644
--- a/docker/.env
+++ b/docker/.env
@@ -1,6 +1,6 @@
 # ======================================================================
 #
-# GENERATED BY init.sh - template: ./templates/dot_env - e2cde05722688ff85d3a93e9cd55787e
+# GENERATED BY init.sh - template: templates/dot_env - e2cde05722688ff85d3a93e9cd55787e
 # values to be used in docker-composer.yml
 #
 # ======================================================================
diff --git a/docker/containers/web-server/Dockerfile b/docker/containers/web-server/Dockerfile
index 5faaf3faacf9e286ff7fdf89b8a9bdf2d8870817..348f987b86cf6ba56ce4fbf20ce9bfff38f99e34 100644
--- a/docker/containers/web-server/Dockerfile
+++ b/docker/containers/web-server/Dockerfile
@@ -1,5 +1,5 @@
 #
-# GENERATED BY init.sh - template: ./templates/web-server-Dockerfile - 42dce773c83597a7d05af398bdd66d15
+# GENERATED BY init.sh - template: templates/web-server-Dockerfile - 42dce773c83597a7d05af398bdd66d15
 #
 FROM php:8.3-apache
 
diff --git a/docker/containers/web-server/apache/sites-enabled/vhost_app.conf b/docker/containers/web-server/apache/sites-enabled/vhost_app.conf
index 872493645c22d783c78e8ae433ce97580abacc5d..776ee05b453a85c32d98c8f3f9461173645e3b4a 100644
--- a/docker/containers/web-server/apache/sites-enabled/vhost_app.conf
+++ b/docker/containers/web-server/apache/sites-enabled/vhost_app.conf
@@ -1,5 +1,5 @@
 #
-# GENERATED BY init.sh - template: ./templates/vhost_app.conf - 9a9cf79de5a3584c0cef6cb79c339c25
+# GENERATED BY init.sh - template: templates/vhost_app.conf - 9a9cf79de5a3584c0cef6cb79c339c25
 #
 
 
diff --git a/docker/containers/web-server/php/extra-php-config.ini b/docker/containers/web-server/php/extra-php-config.ini
index 5cc3af6d4826463f008b05762f398fc227f58b64..472676db8a976999da1b1239095909552595d35f 100644
--- a/docker/containers/web-server/php/extra-php-config.ini
+++ b/docker/containers/web-server/php/extra-php-config.ini
@@ -1,5 +1,5 @@
 ;
-; GENERATED BY init.sh - template: ./templates/extra-php-config.ini - 80c23edaf568e2c36b9926fe2339e481
+; GENERATED BY init.sh - template: templates/extra-php-config.ini - 80c23edaf568e2c36b9926fe2339e481
 ;
 [PHP]
 
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index 2b7f14c6947f0c4046c782abeee668332f8462bc..83c06ec44994fdb672b54f3a3fba230a64dbfeb5 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -1,5 +1,5 @@
 #
-# GENERATED BY init.sh - template: ./templates/docker-compose.yml - d3f1d9971b8c651cfcec647a3de69105
+# GENERATED BY init.sh - template: templates/docker-compose.yml - d3f1d9971b8c651cfcec647a3de69105
 #
 # ======================================================================
 #
diff --git a/docker/init.sh b/docker/init.sh
index 4f9d33eb3428ee7cc75e9530f6e11371af6e3449..9c7c858cb27599c8778a9028e54ca5282cd90d0f 100755
--- a/docker/init.sh
+++ b/docker/init.sh
@@ -20,6 +20,9 @@
 # 2024-07-22  v1.13 <axel.hahn@unibe.ch>      show info if there is no database container; speedup replacements
 # 2024-07-22  v1.14 <axel.hahn@unibe.ch>      show colored boxes with container status
 # 2024-07-24  v1.15 <axel.hahn@unibe.ch>      update menu output
+# 2024-07-26  v1.16 <axel.hahn@unibe.ch>      hide unnecessary menu items (WIP)
+# 2024-07-29  v1.17 <www.axel-hahn.de>        hide unnecessary menu items; reorder functions
+# 2024-08-14  v1.18 <www.axel-hahn.de>        update container view
 # ======================================================================
 
 cd "$( dirname "$0" )" || exit 1
@@ -33,7 +36,7 @@ _self=$( basename "$0" )
 # shellcheck source=/dev/null
 . "${_self}.cfg" || exit 1
 
-_version="1.15"
+_version="1.18"
 
 # git@git-repo.iml.unibe.ch:iml-open-source/docker-php-starterkit.git
 selfgitrepo="docker-php-starterkit.git"
@@ -43,13 +46,65 @@ fgRed="\e[31m"
 fgGreen="\e[32m"
 fgBrown="\e[33m"
 fgBlue="\e[34m"
+
+fgInvert="\e[7m"
 fgReset="\e[0m"
 
+# ----- status varsiables
+# running containers
+DC_WEB_UP=0
+DC_DB_UP=0
+
+# repo of docker-php-starterkit is here?
+DC_REPO=1
+
+DC_CONFIG_CHANGED=0
+
+DC_WEB_URL=""
 
 # ----------------------------------------------------------------------
 # FUNCTIONS
 # ----------------------------------------------------------------------
 
+# ----------------------------------------------------------------------
+# STATUS FUNCTIONS
+
+# get container status and set global variable DC_REPO
+# DC_REPO = 0 nothing to do - repo was changed to project
+# DC_REPO = 1 if repo is in selfgitrepo (must be deleted)
+function _getStatus_repo(){
+    DC_REPO=0
+    git config --get remote.origin.url 2>/dev/null | grep -q $selfgitrepo && DC_REPO=1
+}
+
+# check if any of the templates has a change that must be applied
+function _getStatus_template(){
+    _generateFiles "dryrun"
+}
+
+# get container status and set global variables
+# DC_WEB_UP - web container 
+# DC_DB_UP  - database container
+#   0 = down
+#   1 = up
+function _getStatus_docker(){
+    local _out
+    _out=$( docker-compose -p "$APP_NAME" ps)    
+
+    DC_WEB_UP=0
+    DC_DB_UP=0
+    grep -q "${APP_NAME}-server" <<< "$_out" && DC_WEB_UP=1
+    grep -q "${APP_NAME}-db"     <<< "$_out"  && DC_DB_UP=1
+}
+
+function _getWebUrl(){
+    DC_WEB_URL="$frontendurl"
+    grep -q "${APP_NAME}-server" /etc/hosts && DC_WEB_URL="https://${APP_NAME}-server/"
+}
+
+# ----------------------------------------------------------------------
+# OUTPUT
+
 # draw a headline 2
 function h2(){
     echo
@@ -62,44 +117,70 @@ function h3(){
     echo -e "$fgBlue----- $*$fgReset"
 }
 
-# show help for param -h
+# helper for menu: print an inverted key
+function  _key(){
+    echo -en "\e[4;7m ${1} \e[0m"
+}
+
+# show menu in interactive mode and list keys in help with param -h
+# param  string  optional: set to "all" to show all menu items
 function showMenu(){
-    cat <<EOM
 
-    $( _key g ) - remove git data of starterkit
-    
-    $( _key i ) - init application: set permissions
-    $( _key t ) - generate files from templates
-    $( _key T ) - remove generated files
- 
-    $( _key u ) - startup containers    docker-compose ... up -d
-    $( _key U ) - startup containers    docker-compose ... up -d --build
-    $( _key s ) - shutdown containers   docker-compose stop
-    $( _key r ) - remove containers     docker-compose rm -f
- 
-    $( _key m ) - more infos
-    $( _key o ) - open app [${APP_NAME}] $frontendurl
-    $( _key c ) - console (bash)
-    $( _key p ) - console check with php linter
- 
-    $( _key q ) - quit
-EOM
+    local _bAll=0
+    test -n "$1" && _bAll=1
+
+    local _spacer="    "
+
+    echo
+    if [ $DC_REPO -eq 1 ] || [ $_bAll -eq 1 ]; then
+        echo "${_spacer}$( _key g ) - remove git data of starterkit"
+        echo
+    fi
+    echo "${_spacer}$( _key i ) - init application: set permissions"
+
+    if [ $DC_CONFIG_CHANGED -eq 1 ] || [ $_bAll -eq 1 ]; then
+        echo "${_spacer}$( _key t ) - generate files from templates"
+    fi
+    if [ $DC_CONFIG_CHANGED -eq 0 ] || [ $_bAll -eq 1 ]; then
+        echo "${_spacer}$( _key T ) - remove generated files"
+    fi
+    echo
+    if [ $DC_WEB_UP -eq 0 ] || [ $_bAll -eq 1 ]; then
+        if [ $DC_CONFIG_CHANGED -eq 0 ] || [ $_bAll -eq 1 ]; then
+            echo "${_spacer}$( _key u ) - startup containers    docker-compose ... up -d"
+            echo "${_spacer}$( _key U ) - startup containers    docker-compose ... up -d --build"
+            echo
+            echo "${_spacer}$( _key r ) - remove containers     docker-compose rm -f"
+        fi
+    fi
+    if [ $DC_WEB_UP -eq 1 ] || [ $_bAll -eq 1 ]; then
+        echo "${_spacer}$( _key s ) - shutdown containers   docker-compose stop"
+        echo
+        echo "${_spacer}$( _key m ) - more infos"
+        echo "${_spacer}$( _key o ) - open app [${APP_NAME}] $DC_WEB_URL"
+        echo "${_spacer}$( _key c ) - console (bash)"
+        echo "${_spacer}$( _key p ) - console check with php linter"
+    fi
+    echo
+    echo "${_spacer}$( _key q ) - quit"
+
 }
 function showHelp(){
     cat <<EOH
+
 INITIALIZER FOR DOCKER APP v$_version
 
 A helper script written in Bash to bring up a PHP+Mysql application in docker.
 
-Source : https://git-repo.iml.unibe.ch/iml-open-source/docker-php-starterkit
-Docs   : https://os-docs.iml.unibe.ch/docker-php-starterkit/
-License: GNU GPL 3.0
+📄 Source : https://git-repo.iml.unibe.ch/iml-open-source/docker-php-starterkit
+📗 Docs   : https://os-docs.iml.unibe.ch/docker-php-starterkit/
+📜 License: GNU GPL 3.0
 (c) Institute for Medical Education; University of Bern
 
 
 SYNTAX:
   $_self [-h|-v]
-  $_self [menu key]
+  $_self [menu key [.. menu key N]]
 
 OPTIONS:
   -h   show this help and exit
@@ -110,7 +191,7 @@ MENU KEYS:
   The same keys can be put as parameter to start this action.
   You can add multiples keys to apply multiple actions.
 
-$( showMenu )
+$( showMenu "all" )
 
 EXAMPLES:
 
@@ -122,6 +203,56 @@ EXAMPLES:
 EOH
 }
 
+
+# show urls for app container
+function _showBrowserurl(){
+    echo "In a web browser open:"
+    echo "  $DC_WEB_URL"
+}
+
+# detect + show ports and urls for app container and db container
+function _showInfos(){
+    _showContainers long
+    h2 INFO
+
+    h3 "processes webserver"
+    # docker-compose top
+    docker top "${APP_NAME}-server"
+    if [ ! "$DB_ADD" = "false" ]; then
+        h3 "processes database"
+        docker top "${APP_NAME}-db"
+    fi
+
+    h3 "What to open in browser"
+    if echo >"/dev/tcp/localhost/${APP_PORT}"; then
+        # echo "OK, app port ${APP_PORT} is reachable"
+        # echo
+        _showBrowserurl
+    else
+        echo "ERROR: app port ${APP_PORT} is not available"
+    fi 2>/dev/null
+
+    if [ "$DB_ADD" != "false" ]; then
+        h3 "Check database port"
+        if echo >"/dev/tcp/localhost/${DB_PORT}"; then
+            echo "OK, db port ${DB_PORT} is reachable"
+            echo
+            echo "In a local DB admin tool you can connect it:"
+            echo "  host    : localhost"
+            echo "  port    : ${DB_PORT}"
+            echo "  user    : root"
+            echo "  password: ${MYSQL_ROOT_PASS}"
+        else
+            echo "NO, db port ${DB_PORT} is not available"
+        fi 2>/dev/null
+
+    fi
+    echo
+}
+
+# ----------------------------------------------------------------------
+# ACTIONS
+
 # set acl on local directory
 function _setWritepermissions(){
     h2 "set write permissions on ${gittarget} ..."
@@ -156,7 +287,7 @@ function _removeGitdata(){
     h2 "Remove git data of starterkit"
     echo -n "Current git remote url: "
     git config --get remote.origin.url
-    if git config --get remote.origin.url 2>/dev/null | grep $selfgitrepo >/dev/null; then
+    if git config --get remote.origin.url 2>/dev/null | grep -q $selfgitrepo; then
         echo
         echo -n "Delete local .git and .gitignore? [y/N] > "
         read -r answer
@@ -180,7 +311,7 @@ function _fix_no-db(){
     fi
 }
 
-# helper functiion to generate replacements using sed
+# helper function to generate replacements using sed
 # it loops over all vars in the config file
 # used in _generateFiles
 function _getreplaces(){
@@ -202,15 +333,21 @@ function _getreplaces(){
 # It skips if 
 #   - 1st line is not starting with "# TARGET: filename"
 #   - target file has no updated lines
+# If the 1st parameter is set to "dryrun" it will not generate files.
+# param string dryrun optional: set to "dryrun" to not generate files
 function _generateFiles(){
 
+    local _dryrun="$1"
+    DC_CONFIG_CHANGED=0
+
     # shellcheck source=/dev/null
     . "${_self}.cfg" || exit 1    
 
     params=$( _getreplaces | while read -r line; do echo -n "-e '$line' ";  done )
 
     local _tmpfile=/tmp/newfilecontent$$.tmp
-    h2 "generate files from templates..."
+    
+    test "$_dryrun" = "dryrun" || h2 "generate files from templates..."
     for mytpl in templates/*
     do
         # h3 $mytpl
@@ -220,7 +357,9 @@ function _generateFiles(){
         target=$( head -1 "$mytpl" | grep "^# TARGET:" | cut -f 2- -d ":" | awk '{ print $1 }' )
 
         if [ -z "$target" ]; then
-            echo "SKIP: $mytpl - target was not found in 1st line"
+            if [ "$_dryrun" != "dryrun" ]; then
+                echo "SKIP: $mytpl - target was not found in 1st line"
+            fi
             _doReplace=0
         fi
 
@@ -235,20 +374,28 @@ function _generateFiles(){
             local _md5; _md5=$( md5sum $_tmpfile | awk '{ print $1 }' )
             sed -i "s#{{generator}}#GENERATED BY $_self - template: $mytpl - $_md5#g" $_tmpfile
 
+            # apply all replacements to the tmp file
             eval sed -i "$params" "$_tmpfile" || exit
 
             _fix_no-db $_tmpfile
 
             # echo "changes for $target:"
-            if diff --color=always "../$target"  "$_tmpfile" | grep -v "$_md5" | grep -v "^---" | grep . || [ ! -f "../$target" ]; then
-                echo -n "$mytpl - changes detected - writing [$target] ... "
-                mkdir -p "$( dirname  ../"$target" )" || exit 2
-                mv "$_tmpfile" "../$target" || exit 2
-                echo OK
-                echo
+            if diff --color=always "../$target"  "$_tmpfile" 2>/dev/null | grep -v "$_md5" | grep -v "^---" | grep . || [ ! -f "../$target" ]; then
+                if [ "$_dryrun" = "dryrun" ]
+                then
+                    DC_CONFIG_CHANGED=1
+                else
+                    echo -n "$mytpl - changes detected - writing [$target] ... "
+                    mkdir -p "$( dirname  ../"$target" )" || exit 2
+                    mv "$_tmpfile" "../$target" || exit 2
+                    echo OK
+                    echo
+                fi
             else
                 rm -f $_tmpfile
-                echo "SKIP: $mytpl - Nothing to do."
+                if [ "$_dryrun" != "dryrun" ]; then
+                    echo "SKIP: $mytpl - Nothing to do."
+                fi
             fi
         fi
     done
@@ -278,9 +425,11 @@ function _removeGeneratedFiles(){
     done
 }
 
+
 # show running containers
 function _showContainers(){
     local bLong=$1
+
     local _out
 
     local sUp=".. UP"
@@ -288,56 +437,42 @@ function _showContainers(){
 
     local Status=
     local StatusWeb="$sDown"
-    local StatusDb=""
+    local StatusDb="$sDown"
     local colWeb=
     local colDb=
 
     colDb="$fgRed"
     colWeb="$fgRed"
 
-    _out=$( if [ -z "$bLong" ]; then
-        docker-compose -p "$APP_NAME" ps
-    else
-        # docker ps | grep "$APP_NAME"
-        docker-compose -p "$APP_NAME" ps
-    fi)
+    if [ $DC_WEB_UP -eq 1 ]; then
+        colWeb="$fgGreen"
+        StatusWeb="$sUp"
+    fi
+    
+    if [ $DC_DB_UP -eq 1 ]; then
+        colDb="$fgGreen"
+        StatusDb="$sUp"
+    fi
 
-    h2 CONTAINERS
-    if [ "$( wc -l <<< "$_out" )"  -eq 1 ]; then
-        if [ "$DB_ADD" = "false" ]; then
-            colDb="$fgGray"
-            Status="The web container is not running. This app has no database container."
-        else
-            StatusDb="down"
-            Status="No container is running for <$APP_NAME>."
-        fi
-    else
-        grep -q "${APP_NAME}-server" <<< "$_out" && colWeb="$fgGreen"
-        grep -q "${APP_NAME}-server" <<< "$_out" && StatusWeb="$sUp"
+    if [ "$DB_ADD" = "false" ]; then
+        colDb="$fgGray"
+        local StatusDb=".. N/A"
+        Status="This app has no database container."
+    fi
 
-        grep -q "${APP_NAME}-db" <<< "$_out"  && colDb="$fgGreen"
-        StatusDb="$sDown"
-        grep -q "${APP_NAME}-db" <<< "$_out"  && StatusDb="$sUp"
+    h2 CONTAINERS
 
-        if [ "$DB_ADD" = "false" ]; then
-            colDb="$fgGray"
-            StatusDb=""
-            Status="INFO: This app has no database container."
-        fi
-    fi
+    echo
+    printf "  $colWeb$fgInvert  %-32s  $fgReset   $colDb$fgInvert  %-32s  $fgReset\n"      "WEB ${StatusWeb}"  "DB ${StatusDb}"
+    printf "    %-32s  $fgReset     %-32s  $fgReset\n"      "PHP ${APP_PHP_VERSION}"      "${MYSQL_IMAGE}"
+    printf "    %-32s  $fgReset     %-32s  $fgReset\n"      ":${APP_PORT}"                ":${DB_PORT}"
 
-    printf "$colWeb     __________________________  $colDb   __________________________    $fgReset \n"
-    printf "$colWeb    |  %-22s  |  $colDb |  %-22s  | $fgReset \n" ""                             ""
-    printf "$colWeb    |  %-22s  |  $colDb |  %-22s  | $fgReset \n" "${APP_NAME}-web ${StatusWeb}" "${APP_NAME}-db ${StatusDb}"
-    printf "$colWeb    |  %-22s  |  $colDb |  %-22s  | $fgReset \n" "  PHP ${APP_PHP_VERSION}"     "  ${MYSQL_IMAGE}"
-    printf "$colWeb    |  %-22s  |  $colDb |  %-22s  | $fgReset \n" "  :${APP_PORT}"               "  :${DB_PORT}"
-    printf "$colWeb    |__________________________| $colDb  |__________________________|   $fgReset \n"
+    echo
 
     if [ -n "$Status" ]; then
+        echo "  $Status"
         echo
-        echo "$Status"
     fi
-    echo
 
     if [ -n "$bLong" ]; then
         echo "$_out"
@@ -349,61 +484,6 @@ function _showContainers(){
 
 }
 
-
-# show urls for app container
-function _showBrowserurl(){
-    echo "In a web browser open:"
-    echo "  $frontendurl"
-    if grep "${APP_NAME}-server" /etc/hosts >/dev/null; then
-        echo "  https://${APP_NAME}-server/"
-    fi
-}
-
-# detect + show ports and urls for app container and db container
-function _showInfos(){
-    _showContainers long
-    h2 INFO
-
-    h3 "processes webserver"
-    # docker-compose top
-    docker top "${APP_NAME}-server"
-    if [ ! "$DB_ADD" = "false" ]; then
-        h3 "processes database"
-        docker top "${APP_NAME}-db"
-    fi
-
-    h3 "Check app port"
-    if echo >"/dev/tcp/localhost/${APP_PORT}"; then
-        echo "OK, app port ${APP_PORT} is reachable"
-        echo
-        _showBrowserurl
-    else
-        echo "NO, app port ${APP_PORT} is not available"
-    fi 2>/dev/null
-
-    if [ "$DB_ADD" != "false" ]; then
-        h3 "Check database port"
-        if echo >"/dev/tcp/localhost/${DB_PORT}"; then
-            echo "OK, db port ${DB_PORT} is reachable"
-            echo
-            echo "In a local DB admin tool you can connect it:"
-            echo "  host    : localhost"
-            echo "  port    : ${DB_PORT}"
-            echo "  user    : root"
-            echo "  password: ${MYSQL_ROOT_PASS}"
-        else
-            echo "NO, db port ${DB_PORT} is not available"
-        fi 2>/dev/null
-
-    fi
-    echo
-}
-
-# helper for menu: print an inverted key
-function  _key(){
-    echo -en "\e[4;7m ${1} \e[0m"
-}
-
 # helper: wait for a return key
 function _wait(){
     local _wait=15
@@ -418,13 +498,16 @@ action=$1; shift 1
 
 while true; do
 
+    _getStatus_repo
+    _getStatus_docker
+    _getStatus_template
+    _getWebUrl
+
     if [ -z "$action" ]; then
 
         echo "_______________________________________________________________________________"
         echo
-        echo
-        echo "  ${APP_NAME^^} :: Initializer for docker"
-        echo "                                                                         ______"
+        printf "  %-70s ______\n" "${APP_NAME^^}  ::  Initializer for docker"
         echo "________________________________________________________________________/ $_version"
         echo
 
@@ -524,7 +607,7 @@ while true; do
             ;;
         o) 
             h2 "Open app ..."
-            xdg-open "$frontendurl"
+            xdg-open "$DC_WEB_URL"
             ;;
         q)
             h2 "Bye!"
diff --git a/docs/10_Get_started/20_Installation_with_Docker.md b/docs/10_Get_started/20_Installation_with_Docker.md
index 8ced80c07fd13ba9b91c0f7db4c77a7782800c88..348f0a147bf8024dd9ff30b0b10d21ce0a4b992f 100644
--- a/docs/10_Get_started/20_Installation_with_Docker.md
+++ b/docs/10_Get_started/20_Installation_with_Docker.md
@@ -1,6 +1,7 @@
 # Installation with a local Docker service
 
 For development a docker environment is part of the repository data.
+You can use it for a first view on your own machine before installing it.
 
 ## Requirements
 
diff --git a/docs/10_Get_started/30_Filestructure.md b/docs/10_Get_started/30_Filestructure.md
index 756107f978c7e9f8c5a50c8048e9a39939c9baf3..2274777a15e3ca7e5e614d6c3f61acccf1594ca2 100644
--- a/docs/10_Get_started/30_Filestructure.md
+++ b/docs/10_Get_started/30_Filestructure.md
@@ -53,9 +53,6 @@ Default: /var/www/[YOUR-DOMAIN]/public_html/
 │       └── 4.21.0
 │           └── img
 │               └── network
-├── versions
-│   ├── classes
-│   └── data
 └── webservice
 ```
 
diff --git a/hooks/templates/config_custom.php.erb b/hooks/templates/config_custom.php.erb
index 01e109e323cf18997723d7657af460f1907b7dc9..e128b89b9492e10c972785da2b79bc7824072458 100644
--- a/hooks/templates/config_custom.php.erb
+++ b/hooks/templates/config_custom.php.erb
@@ -103,15 +103,37 @@ return [
     ],
 
     /*
-    'foreman' => array(
+    'foreman' => [
         'api'=>'<%= @replace["foreman-url"] %>', // with ending "/"
         'user'=>'<%= @replace["foreman-user"] %>',
         'password'=>'<%= @replace["foreman-password"] %>',
         'ignore-ssl-error'=><%= @replace["foreman-ignore-ssl-error"] %>,
         // 'varname-replace'=>'ci-replacement',
-    ),
+    ],
     */
 
     // ----------------------------------------------------------------------    
+    // APPMONITOR
+    // see <https://os-docs.iml.unibe.ch/appmonitor/>
+    // ----------------------------------------------------------------------    
+
+    "appmonitor" => [
+
+        // notification
+
+        // email notification for this application
+        "email" => [<%= @replace["appmonitor-email-to"] %>],
+
+        "slack" => [<%= @replace["appmonitor-slack"] %>],
+
+        // limit access to appmonitor client to specific IP addresses
+        "ip" => [<%= @replace["appmonitor-ip"] %>],
+
+        // limit access to appmonitor client to specific token
+        "token" => [<%= @replace["appmonitor-token"] %>],
+
+    ],
+
+    // ----------------------------------------------------------------------    
 
 ];
\ No newline at end of file
diff --git a/public_html/api/index.php b/public_html/api/index.php
index 31063b5ec3020186b873c77161bc7b24198e886a..15c0b25ad5c8cb4889cc86851f94ec17d18d976b 100644
--- a/public_html/api/index.php
+++ b/public_html/api/index.php
@@ -11,245 +11,271 @@
  * ----------------------------------------------------------------------
  * 2020-06-16  v0.9  <axel.hahn@iml.unibe.ch>  
  * 2021-03-29  v1.2  <axel.hahn@iml.unibe.ch>  support slashes in branch names
+ * 2024-09-02  v1.3  <axel.hahn@unibe.ch>      php8 only; added variable types; short array syntax
  * ======================================================================
  */
 
-    $bDebug=false;
-    ini_set('display_errors', 1);
-    ini_set('display_startup_errors', 1);
-    error_reporting(E_ALL);
-
-    require_once("../../config/inc_projects_config.php");
-    
-    $sDirClasses=__DIR__.'/../deployment/classes/';
-    require_once($sDirClasses.'/project.class.php');
-    require_once($sDirClasses.'logger.class.php');
-
-    $iMaxAge=60;
-    
-    // ----------------------------------------------------------------------
-    // FUNCTIONS
-    // ----------------------------------------------------------------------
-    /**
-     * write debug text (if enabled)
-     * @global boolean $bDebug
-     * @param string  $s       message
-     * @param string  $sLevel  level; one of info|
-     * @return boolean
-     */
-    function _wd($s, $sLevel='info'){
-        global $bDebug;
-        if ($bDebug){
-            echo '<div class="debug debug-'.$sLevel.'">DEBUG: '.$s.'</div>';
-        }
-        return true;
+$bDebug = false;
+ini_set('display_errors', 1);
+ini_set('display_startup_errors', 1);
+error_reporting(E_ALL);
+
+/**
+ * Path to deployment classes
+ * @var string
+ */
+ $sDirClasses = __DIR__ . '/../deployment/classes/';
+
+ /**
+ * Allowed time delta for client or server
+ * @var integer
+ */
+$iMaxAge = 60;
+
+require_once("../../config/inc_projects_config.php");
+
+require_once($sDirClasses . '/project.class.php');
+require_once($sDirClasses . 'logger.class.php');
+
+// ----------------------------------------------------------------------
+// FUNCTIONS
+// ----------------------------------------------------------------------
+/**
+ * Write debug text (if enabled)
+ * 
+ * @global boolean $bDebug
+ * 
+ * @param string  $s       message
+ * @param string  $sLevel  level; one of info|
+ * @return boolean
+ */
+function _wd(string $s, string $sLevel = 'info'): bool
+{
+    global $bDebug;
+    if ($bDebug) {
+        echo "<div class=\"debug debug-$sLevel\">DEBUG: $s</div>";
     }
-    
-    /**
-     * abort execution with error
-     * @param string   $s        message
-     * @param integer  $iStatus  http status code to send
-     */
-    function _quit($s, $iStatus=400){
-        $aStatus=array(
-           400=>'HTTP/1.0 400 Bad Request', 
-           403=>'HTTP/1.0 403 Access denied', 
-           404=>'HTTP/1.0 404 Not found', 
-        );
-        header($aStatus[$iStatus]);
-        _done(array('status'=>$iStatus, 'info'=>$aStatus[$iStatus], 'message'=>$s));
+    return true;
+}
+
+/**
+ * Abort execution of API requestwith error
+ * 
+ * @param string   $s        message
+ * @param integer  $iStatus  http status code to send
+ */
+function _quit(string $s, int $iStatus = 400): void
+{
+    $aStatus = [
+        400 => 'HTTP/1.0 400 Bad Request',
+        403 => 'HTTP/1.0 403 Access denied',
+        404 => 'HTTP/1.0 404 Not found',
+    ];
+    header($aStatus[$iStatus]);
+    _done(['status' => $iStatus, 'info' => $aStatus[$iStatus], 'message' => $s]);
+}
+
+/**
+ * End with OK output
+ * 
+ * @param array $Data  array data to show as JSON
+ * @return void
+ */
+function _done(array $Data): void
+{
+    echo is_array($Data)
+        ? json_encode($Data, JSON_PRETTY_PRINT)
+        : $Data
+    ;
+    die();
+}
+
+/**
+ * Check authorization in the http request header and age of timestamp
+ * On a failed check the request will be terminated
+ * 
+ * @global int $iMaxAge         max allowed age
+ * 
+ * @param string $sProjectSecret
+ * @return boolean
+ */
+function _checkAuth(string $sProjectSecret): bool
+{
+    global $iMaxAge;
+    $aReqHeaders = apache_request_headers();
+    _wd('<pre>' . print_r($aReqHeaders, 1) . '</pre>');
+    if (!isset($aReqHeaders['Authorization'])) {
+        _quit('Access denied. Missing authorization.', 403);
     }
-    /**
-     * end with OK output
-     * @param type $Data
-     */
-    function _done($Data){
-        echo is_array($Data) 
-            ? json_encode($Data, JSON_PRETTY_PRINT)
-            : $Data
-            ;
-        die();
+    if (!isset($aReqHeaders['Date'])) {
+        _quit('Access denied. Missing field "Date:" in the request header.', 403);
     }
-    
-    /**
-     * Check authorization in the http request header and age of timestamp
-     * On a failed check the request will be terminated
-     * @global int $iMaxAge         max allowed age
-     * @param string $sProjectSecret
-     * @return boolean
-     */
-    function _checkAuth($sProjectSecret){
-        global $iMaxAge;
-        $aReqHeaders=apache_request_headers();
-        _wd('<pre>'.print_r($aReqHeaders, 1).'</pre>');
-        if(!isset($aReqHeaders['Authorization'])){
-            _quit('Access denied. Missing authorization.', 403);
-        }         
-        if(!isset($aReqHeaders['Date'])){
-            _quit('Access denied. Missing field "Date:" in the request header.', 403);
-        }         
-
-        $sGotHash= preg_replace('/^.*\:/', '', $aReqHeaders['Authorization']);
-        $sGotDate= $aReqHeaders['Date'];
-        $sGotMethod=$_SERVER['REQUEST_METHOD'];
-        $sGotReq=$_SERVER['REQUEST_URI'];
-
-
-        $sMyData="$sGotMethod\n$sGotReq\n$sGotDate\n";
-        $sMyHash= base64_encode(hash_hmac("sha1", $sMyData, $sProjectSecret));
-
-        _wd('Hash: '.$sGotHash.' -- from header');
-        _wd('Hash: '.$sMyHash.' -- rebuilt');
-        if($sGotHash!==$sMyHash){
-            _quit('Access denied. Invalid hash.', 403);
-        }
 
-        $iAge=date('U')-date('U', strtotime($sGotDate));
-        _wd('Date: '.$sGotDate.' - age: '.$iAge.' sec');
-        if($iAge>$iMaxAge){
-            _quit('Access denied. Hash is out of date: '.$iAge. ' sec is older '.$iMaxAge.' sec. Maybe client or server is out of sync.', 403);
-        }
-        if($iAge<-$iMaxAge){
-            _quit('Access denied. Hash is '.$iAge. ' sec in future but only '.$iMaxAge.' sec are allowed. Maybe client or server is out of sync.', 403);
-        }
-        return true;
+    $sGotHash = preg_replace('/^.*\:/', '', $aReqHeaders['Authorization']);
+    $sGotDate = $aReqHeaders['Date'];
+    $sGotMethod = $_SERVER['REQUEST_METHOD'];
+    $sGotReq = $_SERVER['REQUEST_URI'];
+
+
+    $sMyData = "$sGotMethod\n$sGotReq\n$sGotDate\n";
+    $sMyHash = base64_encode(hash_hmac("sha1", $sMyData, $sProjectSecret));
+
+    _wd('Hash: ' . $sGotHash . ' -- from header');
+    _wd('Hash: ' . $sMyHash . ' -- rebuilt');
+    if ($sGotHash !== $sMyHash) {
+        _quit('Access denied. Invalid hash.', 403);
+    }
+
+    $iAge = date('U') - date('U', strtotime($sGotDate));
+    _wd('Date: ' . $sGotDate . ' - age: ' . $iAge . ' sec');
+    if ($iAge > $iMaxAge) {
+        _quit('Access denied. Hash is out of date: ' . $iAge . ' sec is older ' . $iMaxAge . ' sec. Maybe client or server is out of sync.', 403);
     }
-    // ----------------------------------------------------------------------
-    // MAIN
-    // ----------------------------------------------------------------------
-    if (!$bDebug){
-        header('Content-Type: application/json');
+    if ($iAge < -$iMaxAge) {
+        _quit('Access denied. Hash is ' . $iAge . ' sec in future but only ' . $iMaxAge . ' sec are allowed. Maybe client or server is out of sync.', 403);
     }
-    _wd('Start: '.date('Y-m-d H:i:s').'<style>body{background:#eee; color:#456;}
+    return true;
+}
+// ----------------------------------------------------------------------
+// MAIN
+// ----------------------------------------------------------------------
+if (!$bDebug) {
+    header('Content-Type: application/json');
+}
+_wd('Start: ' . date('Y-m-d H:i:s') . '<style>body{background:#eee; color:#456;}
             .debug{background:#ddd; margin-bottom: 2px;}
          </style>');
 
-    _wd('request uri is '.$_SERVER["REQUEST_URI"]); 
-    _wd('<pre>GET: '.print_r($_GET, 1).'</pre>');
-
-    
-    // ---------- SPLIT URL
-    
-    $aUriSplit= explode('/', preg_replace('/\?.*$/', '', $_SERVER["REQUEST_URI"]));
-
-    array_shift($aUriSplit);
-    array_shift($aUriSplit);
-    _wd('<pre>$aUriSplit: '.print_r($aUriSplit, 1).'</pre>');  
-    /*
-    
-    /api/v1/projects/ci/build/...
-    $aUriSplit: Array
-        (
-            [0] => v1
-            [1] => projects
-            [2] => ci
-            [3] => build
-        )
-     */
-    $sApiVersion = isset($aUriSplit[0]) ? $aUriSplit[0] : false;
-    $sApiItem    = isset($aUriSplit[1]) ? $aUriSplit[1] : false;
-
-
-    if(!$sApiVersion){
-        _quit('ERROR: no param for api version was found.');
-    }
-    if(!$sApiItem){
-        _quit('ERROR: no param for item was found.');
-    }
-    
-    
-    switch ($sApiVersion){
-        case 'v1':
-            switch($sApiItem){
-                case 'projects':
-
-                    $oProject=new project();
-                    $aList=$oProject->getProjects();
-                    _wd('<pre>'.print_r($aList,1).'</pre>');
-                    _done($aList);
-                    break;;
-                    
-                case 'project':
-                    // path /api/v1/project
-
-                    $sPrjId     = isset($aUriSplit[2]) ? $aUriSplit[2] : false;
-                    $sPrjAction = isset($aUriSplit[3]) ? $aUriSplit[3] : false;
-                    $sBranch    = implode('/', array_slice($aUriSplit, 4));
-                    
-                    // $sParam4    = isset($aUriSplit[4]) ? $aUriSplit[4] : false;
-                    // $sParam5    = isset($aUriSplit[5]) ? $aUriSplit[5] : false;
-                    $sMethod    = $_SERVER['REQUEST_METHOD'];
-                    _wd('$sPrjId = '.$sPrjId);
-                    _wd('$sPrjAction = '.$sPrjAction);
-                    _wd('$sBranch = ' . $sBranch); 
-                    
-                    $oCLog = new logger();
-                    // try to init the given project
-                    try{
-                        ob_start();
-                        $oProject=new project($sPrjId);
-                        
-                        // $oProject->setProjectById($sPrjId);
-                        ob_end_clean();
-                            
-                    } catch (Exception $exc) {
-                        _quit('ERROR: project with id ['.$sPrjId.'] does not exist.', 404);
-                    }
-
-                    // get secret
-                    $aPrjCfg=$oProject->getConfig();
-                    $sProjectSecret=isset($aPrjCfg['api']['secret']) ? $aPrjCfg['api']['secret'] : false;
-                    if(!$sProjectSecret){
-                        _quit('Access denied. API access is disabled.');
-                    }
-
-                    // check authorization 
-                    _checkAuth($sProjectSecret);
-
-                    // echo "OK: request was authorized successfully.\n";
-
-                    $oProject->oUser->setUser('api');
-
-                    switch($sPrjAction){
-                        case "build":
-                            if ($sBranch){
-                                $aResult=$oProject->setBranchname($sBranch);
-                            }
-                            $sBranchname=$oProject->getBranchname();
-                            $aRepodata = $oProject->getRemoteBranches(true); // ignore cache = true
-                            if(!isset($aRepodata[$sBranchname])){
-                                _quit('ERROR: branch not found: '.$sBranchname, 404);
-                            }
-                            
-                            
-                            // echo "branch is set to ".$oProject->getBranchname()."\n";
-                            if ($sMethod==='GET'){
-                                $sNext=$oProject->getNextPhase();
-                                _done(array(
-                                    'branch'=>$sBranchname,
-                                    'phase'=>$sNext,
-                                    'repo'=>$aRepodata[$sBranchname]
-                                ));
-                            }
-                            if ($sMethod==='POST'){
-                                echo "starting build() ...";
-                                flush();
-                                echo $oProject->build();
-                            }
-                            break;;
-                        case "phases":
-                            _done($oProject->getAllPhaseInfos());
-                            break;;
-                        default:
-                            _quit('ERROR: Wrong action ['.$sApiItem.'].');
-                    }
-      
-                    break;;
-                    
-                default:
-                    _quit('ERROR: item ['.$sApiItem.'] is invalid.');
-            }
-            break;
-        default:
-            _quit('ERROR: Wrong (unsupported) api version.');
-    }
\ No newline at end of file
+_wd('request uri is ' . $_SERVER["REQUEST_URI"]);
+_wd('<pre>GET: ' . print_r($_GET, 1) . '</pre>');
+
+
+// ---------- SPLIT URL
+
+$aUriSplit = explode('/', preg_replace('/\?.*$/', '', $_SERVER["REQUEST_URI"]));
+
+array_shift($aUriSplit);
+array_shift($aUriSplit);
+_wd('<pre>$aUriSplit: ' . print_r($aUriSplit, 1) . '</pre>');
+/*
+
+/api/v1/projects/ci/build/...
+$aUriSplit: Array
+    (
+        [0] => v1
+        [1] => projects
+        [2] => ci
+        [3] => build
+    )
+ */
+$sApiVersion = isset($aUriSplit[0]) ? $aUriSplit[0] : false;
+$sApiItem = isset($aUriSplit[1]) ? $aUriSplit[1] : false;
+
+
+if (!$sApiVersion) {
+    _quit('ERROR: no param for api version was found.');
+}
+if (!$sApiItem) {
+    _quit('ERROR: no param for item was found.');
+}
+
+
+switch ($sApiVersion) {
+    case 'v1':
+        switch ($sApiItem) {
+            case 'projects':
+
+                $oProject = new project();
+                $aList = $oProject->getProjects();
+                _wd('<pre>' . print_r($aList, 1) . '</pre>');
+                _done($aList);
+                break;
+                ;
+
+            case 'project':
+                // path /api/v1/project
+
+                $sPrjId = isset($aUriSplit[2]) ? $aUriSplit[2] : false;
+                $sPrjAction = isset($aUriSplit[3]) ? $aUriSplit[3] : false;
+                $sBranch = implode('/', array_slice($aUriSplit, 4));
+
+                // $sParam4    = isset($aUriSplit[4]) ? $aUriSplit[4] : false;
+                // $sParam5    = isset($aUriSplit[5]) ? $aUriSplit[5] : false;
+                $sMethod = $_SERVER['REQUEST_METHOD'];
+                _wd('$sPrjId = ' . $sPrjId);
+                _wd('$sPrjAction = ' . $sPrjAction);
+                _wd('$sBranch = ' . $sBranch);
+
+                $oCLog = new logger();
+                // try to init the given project
+                try {
+                    ob_start();
+                    $oProject = new project($sPrjId);
+
+                    // $oProject->setProjectById($sPrjId);
+                    ob_end_clean();
+
+                } catch (Exception $exc) {
+                    _quit('ERROR: project with id [' . $sPrjId . '] does not exist.', 404);
+                }
+
+                // get secret
+                $aPrjCfg = $oProject->getConfig();
+                $sProjectSecret = isset($aPrjCfg['api']['secret']) ? $aPrjCfg['api']['secret'] : false;
+                if (!$sProjectSecret) {
+                    _quit('Access denied. API access is disabled.');
+                }
+
+                // check authorization 
+                _checkAuth($sProjectSecret);
+
+                // echo "OK: request was authorized successfully.\n";
+
+                $oProject->oUser->setUser('api');
+
+                switch ($sPrjAction) {
+                    case "build":
+                        if ($sBranch) {
+                            $aResult = $oProject->setBranchname($sBranch);
+                        }
+                        $sBranchname = $oProject->getBranchname();
+                        $aRepodata = $oProject->getRemoteBranches(true); // ignore cache = true
+                        if (!isset($aRepodata[$sBranchname])) {
+                            _quit('ERROR: branch not found: ' . $sBranchname, 404);
+                        }
+
+
+                        // echo "branch is set to ".$oProject->getBranchname()."\n";
+                        if ($sMethod === 'GET') {
+                            $sNext = $oProject->getNextPhase();
+                            _done([
+                                'branch' => $sBranchname,
+                                'phase' => $sNext,
+                                'repo' => $aRepodata[$sBranchname]
+                            ]);
+                        }
+                        if ($sMethod === 'POST') {
+                            echo "starting build() ...";
+                            flush();
+                            echo $oProject->build();
+                        }
+                        break;
+                        ;
+                    case "phases":
+                        _done($oProject->getAllPhaseInfos());
+                        break;
+                        ;
+                    default:
+                        _quit('ERROR: Wrong action [' . $sApiItem . '].');
+                }
+
+                break;
+                ;
+
+            default:
+                _quit('ERROR: item [' . $sApiItem . '] is invalid.');
+        }
+        break;
+    default:
+        _quit('ERROR: Wrong (unsupported) api version.');
+}
\ No newline at end of file
diff --git a/public_html/appmonitor/general_include.php b/public_html/appmonitor/general_include.php
index bdd129b19018a17c8f27d750d304e9198ccf12bb..b7530759bed2059dace5d912f142a0816c344d6e 100644
--- a/public_html/appmonitor/general_include.php
+++ b/public_html/appmonitor/general_include.php
@@ -7,6 +7,7 @@
  * @author: Axel Hahn
  * ----------------------------------------------------------------------
  * 2018-06-30  v0.1
+ * 2024-09-04  php8 only: short array syntax; use data from ci server config file
  */
 
 // ----------------------------------------------------------------------
@@ -17,28 +18,31 @@
 
 // check local ips and IML networks (includes the monitor)
 // appmonitor is not available on EDUROAM or VPN
-$oMonitor->checkIp(array(
-    '127.0.0.1',
-    '::1',
-    '10.0.2.2',
-    '130.92.30.11',
-    '130.92.30.44',
-    '130.92.79.49',
-));
+if(isset($aConfig['appmonitor']['ip']) && count($aConfig['appmonitor']['ip'])){
+    $oMonitor->checkIp($aConfig['appmonitor']['ip']);
+}
 
 // --- check a token
 // an incoming request must have the GET param "token=123"
 // $oMonitor->checkTokem('token', '123');
-
+if(isset($aConfig['appmonitor']['token']) && count($aConfig['appmonitor']['token'])){
+    $oMonitor->checkToken($aConfig['appmonitor']['token'][0], $aConfig['appmonitor']['token'][1]);
+}
 
 // ----------------------------------------------------------------------
 // NOTIFICATION
 // ----------------------------------------------------------------------
 
-// $oMonitor->addEmail('sysadmin@example.com');
-// $oMonitor->addSlackWebhook(array("mywebhook"=> "https://hooks.slack.com/services/(...)"));
-$oMonitor->addEmail('axel.hahn@iml.unibe.ch');
-
+if(isset($aConfig['appmonitor']['email']) && count($aConfig['appmonitor']['email'])){
+    foreach($aConfig['appmonitor']['email'] as $sEmailTo){
+        $oMonitor->addEmail($sEmailTo);
+    }
+}
+if(isset($aConfig['appmonitor']['slack']) && count($aConfig['appmonitor']['slack'])){
+    foreach($aConfig['appmonitor']['slack'] as $aSlackTarget){
+        $oMonitor->addSlackWebhook($aSlackTarget[0], $aSlackTarget[1]);
+    }
+}
 
 // ----------------------------------------------------------------------
 // set a tag with phase
@@ -46,7 +50,9 @@ $oMonitor->addEmail('axel.hahn@iml.unibe.ch');
 $sHost=$_SERVER['HTTP_HOST'];
 $sHost2=php_uname("n");   
 $sMyPhase='live';
-foreach (array('dev', 'preview', 'stage', 'demo') as $sPhase){
+
+// foreach (array('dev', 'preview', 'stage', 'demo') as $sPhase){
+foreach (array_keys($aConfig['phases']) as $sPhase){
     if(
         strstr($sHost.'.', $sPhase)!==false
         || strstr($sHost.'-', $sPhase)!==false
diff --git a/public_html/appmonitor/git_update_appmonitor.sh b/public_html/appmonitor/git_update_appmonitor.sh
index 32f5b785e927960c8c393e213c4fdb1481206d2b..117d5084dc6473c2199cb3c23dcb070ba8e6462a 100755
--- a/public_html/appmonitor/git_update_appmonitor.sh
+++ b/public_html/appmonitor/git_update_appmonitor.sh
@@ -1,7 +1,19 @@
 #!/bin/bash
 # ======================================================================
 #
-#   A P P M O N I T O R  ::  CLIENT - UPDATE
+#   
+#    _____ _____ __                   _____         _ _           
+#   |     |     |  |      ___ ___ ___|     |___ ___|_| |_ ___ ___ 
+#   |-   -| | | |  |__   | .'| . | . | | | | . |   | |  _| . |  _|
+#   |_____|_|_|_|_____|  |__,|  _|  _|_|_|_|___|_|_|_|_| |___|_|  
+#                            |_| |_|                              
+#                             _ _         _                                            
+#                         ___| |_|___ ___| |_                                          
+#                        |  _| | | -_|   |  _|                                         
+#                        |___|_|_|___|_|_|_|   
+#                                                                 
+#
+#                         INSTALLER + UPDATER
 #
 # This script will install or update the appmonitor client only.
 #
@@ -16,6 +28,7 @@
 # 2022-04-12  0.2  <axel.hahn@iml.unibe.ch>  add help; exclude unneeded files
 # 2022-05-03  0.3  <axel.hahn@iml.unibe.ch>  create general_include.php
 # 2024-07-25  0.4  <axel.hahn@iml.unibe.ch>  update quoting and comments
+# 2024-07-31  0.5  <axel.hahn@iml.unibe.ch>  Show more helpful information; wait on 1st install; added param -n
 # ======================================================================
 
 # ----------------------------------------------------------------------
@@ -23,12 +36,16 @@
 # ----------------------------------------------------------------------
 
 readonly git_repo_url="https://github.com/iml-it/appmonitor.git"
+readonly docs_url="https://os-docs.iml.unibe.ch/appmonitor/PHP_client/index.html"
 readonly line="______________________________________________________________________________"
-readonly version="0.4"
+readonly version="0.5"
 
 git_target=/tmp/git_data__appmonitor
 client_from="${git_target}/public_html/client"
 client_to="."
+isUpdate=0
+wait=1
+
 
 cd "$( dirname "$0" )" || exit 1
 
@@ -88,20 +105,22 @@ function _gitUpdate(){
 # ----------------------------------------------------------------------
 
 cat <<ENDOFHEADER
+$line
+
+    IML Appmonitor client   ::   installer + updater  v$version
+$line
 
-          +-----------------------------------+
-          |                                   |
-          |  INSTALLER  |                     |
-          |      +      |  Appmonitor client  |
-          |   UPDATER   |                     |
-          |                                   |
-          +--------------------------- v$version --+
 
 ENDOFHEADER
 
 case "$1" in
     -h|--help)
         cat <<ENDOFHELP
+    The IML Appmonitor is free software.
+
+        Source: https://github.com/iml-it/appmonitor
+        Docs: https://os-docs.iml.unibe.ch/appmonitor
+        License: GNU GPL 3.0
 
     This is a helper script to get the files of the IML Appmonitor
     client part only.
@@ -116,18 +135,26 @@ case "$1" in
     On additional runs it updates the files.
 
     USAGE:
+        $0 [OPTIONS] [TARGET]
 
-    $0 [target path]
+    OPTIONS:
+        -h|--help
+            Show this help and exit
+        -n|--nowait
+            Do not wait for RETURN on 1st installation.
+            Use it for an unattended installation.
 
-        default target is [.] (current directory)
-
-    $0 -h|--help
-
-        Show this help.
+    PARAMETERS:
+        TARGET 
+            optional target path for the client files
+            default target is "." (current directory)
 
 ENDOFHELP
         exit 0
         ;;
+    -n|--nowait)
+        wait=0
+        ;;
     *)
         if test -n "$1" 
             then
@@ -144,6 +171,32 @@ esac
 which rsync >/dev/null || exit 1
 which git >/dev/null || exit 1
 
+test -f general_include.php && isUpdate=1
+
+if [ $isUpdate -eq 0 ]; then
+    cat <<WELCOME
+    Welcome to the Appmonitor client installation!
+
+
+    This is a helper script to get the client files of the IML Appmonitor.
+    They will be installed into the directory "$client_to" $( test "$client_to" = "." && (echo; echo -n "    "; pwd) )
+
+        If this is not correct, press Ctrl + C to abort and use a
+        parameter to set another target directory.
+
+        "$( basename "$0" ) -h" shows a help and more options.
+
+
+WELCOME
+    if [ $wait -eq 1 ]; then
+        echo -n "    RETURN to continue ... "
+        read -r
+    fi
+else
+    echo "Updating local files ..."
+fi
+echo
+
 echo $line
 echo ">>> #1 of 3 >>> update local git data"
 echo
@@ -179,7 +232,32 @@ echo
 diff --color -r "$client_from" "$client_to"
 echo
 
+if [ $isUpdate -eq 0 ]; then
+    _fileupdate index.sample.php
+    cat <<INTRODUCTION
+$line
+
+
+    DONE!
+    The Appmonitor client was installed.
+
+    - Please edit index.php and general_include.php.
 
+    - If you have multiple applications below webroot then you can 
+      rename the file index.php to check-[appname].php eg.
+      check-cms.php, check-blog.php, ... 
+
+    - Start "$( basename "$0" )" again to perform an update.
+      Maybe you want to create a cronjob for this.
+
+INTRODUCTION
+else
+    echo "Appmonitor client was updated."
+fi
+echo
+
+echo "Documentation: $docs_url"
+echo
 echo $line
 echo done.
 
diff --git a/public_html/appmonitor/index.php b/public_html/appmonitor/index.php
index 15011824790473dfd0a5dbd6d1d82c227ef4566f..422c047b54f93e304b1a7d104fe718326e08edc4 100644
--- a/public_html/appmonitor/index.php
+++ b/public_html/appmonitor/index.php
@@ -1,46 +1,69 @@
 <?php
-
+/**
+ * IML APPMONITOR CHECKS FOR CI SERVER
+ * 
+ * @author: Axel Hahn
+ * 
+ * ------------------------------------------------------------------
+ * 2014-10-24  v0.1
+ * ...
+ * 2024-09-04  php8 only: short array syntax
+ */
 require_once('classes/appmonitor-client.class.php');
+
+
 require_once(__DIR__.'/../deployment/classes/project.class.php');
 $oMonitor = new appmonitor();
-    
-$oMonitor->addCheck(
-        array(
-            "name" => "simple",
-            "description" => "Very simple test",
-            "check" => array(
-                "function" => "Simple",
-                "params" => array(
-                    "result" => 0,
-                    "value" => "The appmonitor client is reachable.",
-                ),
-            ),
-        )
-);
 
 $sCfgfile='../../config/inc_projects_config.php';
+require_once $sCfgfile;
+
+include('general_include.php');
 
 
 // ----------------------------------------------------------------------
-// config file
+// config files
 // ----------------------------------------------------------------------
 
 $oMonitor->addCheck(
-    array(
+    [
         "name" => "read config file",
         "description" => "Check if config file is readable",
-        "check" => array(
+        "check" => [
             "function" => "File",
-            "params" => array(
+            "params" => [
                 "filename" => $sCfgfile,
                 "file"     => true,
                 "readable" => true,
-            ),
-        ),
-    )
+            ],
+        ],
+    ]
 );
 
-require_once $sCfgfile;
+
+
+foreach ([
+    "config/config_custom.php",
+    "config/config_defaults.php",
+    "config/inc_roles.php",
+] as $sCfgfile) {
+
+    $oMonitor->addCheck(
+        [
+            "name" => "read config file $sCfgfile",
+            "description" => "Check if config file $sCfgfile is an existing file and is readable",
+            "check" => [
+                "function" => "File",
+                "params" => [
+                    "filename" => "../../$sCfgfile",
+                    "file"     => true,
+                    "readable" => true,
+                ],
+            ],
+        ]
+    );
+}
+
 
 // echo '<pre>' . print_r($aConfig, 1) . '</pre>';die();
 
@@ -48,50 +71,50 @@ require_once $sCfgfile;
 // directories
 // ----------------------------------------------------------------------
 
-foreach (array(
+foreach ([
     
-    'tmpDir'=>array('dir'=>$aConfig['tmpDir'], 'descr'=>'Temp Dir mit git Daten'),
-    'configDir'=>array('dir'=>$aConfig['configDir'], 'descr'=>'Ablage der Programm-Config'),
-    'dataDir'=>array('dir'=>$aConfig['dataDir'], 'descr'=>'Basisverzeichnis fue DB, Projekt-Configs, SSH-Keys'),
-        'dataDir/database'=>array('dir'=>$aConfig['dataDir'].'/database', 'descr'=>'DB-Ablage (Sqlite)'),
-        'dataDir/projects'=>array('dir'=>$aConfig['dataDir'].'/projects', 'descr'=>'Projekt-Configdateien'),
-        'dataDir/sshkeys'=>array('dir'=>$aConfig['dataDir'].'/sshkeys', 'descr'=>'SSH Keys'),
+    'tmpDir'=>['dir'=>$aConfig['tmpDir'], 'descr'=>'Temp Dir mit git Daten'],
+    'configDir'=>['dir'=>$aConfig['configDir'], 'descr'=>'Ablage der Programm-Config'],
+    'dataDir'=>['dir'=>$aConfig['dataDir'], 'descr'=>'Basisverzeichnis fue DB, Projekt-Configs, SSH-Keys'],
+        'dataDir/database'=>['dir'=>$aConfig['dataDir'].'/database', 'descr'=>'DB-Ablage (Sqlite)'],
+        'dataDir/projects'=>['dir'=>$aConfig['dataDir'].'/projects', 'descr'=>'Projekt-Configdateien'],
+        'dataDir/sshkeys'=>['dir'=>$aConfig['dataDir'].'/sshkeys', 'descr'=>'SSH Keys'],
     
-    'buildDir'=>array('dir'=>$aConfig['buildDir'], 'descr'=>'Basisverzeichnis fuer Builds'),
-    'packageDir'=>array('dir'=>$aConfig['packageDir'], 'descr'=>'Basisverzeichnis der Pakete und Versionen'),
-    'archiveDir'=>array('dir'=>$aConfig['archiveDir'], 'descr'=>'Ablage der gebuildeten Archive'),
+    'buildDir'=>['dir'=>$aConfig['buildDir'], 'descr'=>'Basisverzeichnis fuer Builds'],
+    'packageDir'=>['dir'=>$aConfig['packageDir'], 'descr'=>'Basisverzeichnis der Pakete und Versionen'],
+    'archiveDir'=>['dir'=>$aConfig['archiveDir'], 'descr'=>'Ablage der gebuildeten Archive'],
     
-) as $sKey=>$aItem) {
+] as $sKey=>$aItem) {
     $oMonitor->addCheck(
-        array(
+        [
             "name" => "dir $sKey",
             "description" => $aItem['descr'],
             "parent" => "read config file",
-            "check" => array(
+            "check" => [
                 "function" => "File",
-                "params" => array(
+                "params" => [
                     "filename" => $aItem['dir'],
                     "dir"      => true,
                     "writable" => true,
-                ),
-            ),
-        )
+                ],
+            ],
+        ]
     );
 }
 $oMonitor->addCheck(
-	array(
+	[
 		"name" => "Free space in Archive dir ",
 		"description" => "The file storage must have some space left",
-		"check" => array(
+		"check" => [
 			"function" => "Diskfree",
             "parent" => "read config file",
-			"params" => array(
+			"params" => [
 				"directory" => $aConfig['archiveDir'],
 				"warning"   => "2GB",
 				"critical"  => "500MB",
-			),
-		),
-	)
+            ],
+		],
+    ]
 );
 // ----------------------------------------------------------------------
 // count of Projects
@@ -113,55 +136,55 @@ foreach ($oPrj->getProjects() as $sPrj) {
         $iInQueue+=$aProgress['hasQueue'] ? 1 : 0;
 }
 $oMonitor->addCheck(
-    array(
+    [
         "name" => "ci projects",
         "description" => "Count of Projects in CI Webgui",
         "group" => "monitor",
         "parent" => "read config file",
-        "check" => array(
+        "check" => [
             "function" => "Simple",
-            "params" => array(
+            "params" => [
                 "result" => $iProjectCount ? RESULT_OK : RESULT_ERROR,
                 "value" => "found projects: $iProjectCount",
                 "count" => $iProjectCount,
                 "visual" => "simple",
-            ),
-        ),
-    )
+            ],
+		],
+    ]
 );
 $oMonitor->addCheck(
-    array(
+    [
         "name" => "ci inProgress",
         "description" => "Projects in progress",
         "group" => "monitor",
         "parent" => "read config file",
-        "check" => array(
+        "check" => [
             "function" => "Simple",
-            "params" => array(
+            "params" => [
                 "result" => RESULT_OK,
                 "value" => "found projects: $iInProgress",
                 "count" => $iInProgress,
                 "visual" => "simple",
-            ),
-        ),
-    )
+            ],
+		],
+    ]
 );
 $oMonitor->addCheck(
-    array(
+    [
         "name" => "ci hasqueue",
         "description" => "Waiting for install",
         "group" => "monitor",
         "parent" => "read config file",
-        "check" => array(
+        "check" => [
             "function" => "Simple",
-            "params" => array(
+            "params" => [
                 "result" => RESULT_OK,
                 "value" => "found projects: $iInQueue",
                 "count" => $iInQueue,
                 "visual" => "simple",
-            ),
-        ),
-    )
+            ],
+		],
+    ]
 );
 
 include 'plugins/apps/shared_check_ssl.php';
@@ -176,32 +199,32 @@ if(isset($aConfig['foreman']['api'])){
     require_once(__DIR__.'/../deployment/classes/foremanapi.class.php');
     $oForeman = new ForemanApi($aConfig['foreman']);
 
-    foreach (array('hostgroups', 'hosts') as $sForemanKey){
-        $aFData=$oForeman->read(array(
-            'request' => array(
-                array($sForemanKey),
-            ),
-            'response' => array(
+    foreach (['hostgroups', 'hosts'] as $sForemanKey){
+        $aFData=$oForeman->read([
+            'request' => [
+                [$sForemanKey],
+            ],
+            'response' => [
                 'id', 'title'
-            ),
-        ));
+            ],
+        ]);
         
         $oMonitor->addCheck(
-            array(
+            [
                 "name" => "Foreman $sForemanKey",
                 "description" => "Count of Foreman $sForemanKey",
                 "group" => "monitor",
                 "parent" => "read config file",
-                "check" => array(
+                "check" => [
                     "function" => "Simple",
-                    "params" => array(
+                    "params" => [
                         "result" => count($aFData) ? RESULT_OK : RESULT_ERROR,
                         "value" => "Count of found Foreman $sForemanKey: " . count($aFData)."; response status: ".$oForeman->getResponseStatus() . "; Http-Code ".$oForeman->getResponseInfo('http_code'),
                         "count" => count($aFData),
                         "visual" => "simple",
-                    ),
-                ),
-            )
+                    ],
+                ],
+            ]
         );
     }
 }
@@ -220,16 +243,16 @@ if(isset($aConfig['plugins']['rollout']['awx'])){
     }
 
     $oMonitor->addCheck(
-        array(
+        [
             "name" => "AWX API",
             "description" => "check if AWX api is available",
             "group" => "network",
             "parent" => "read config file",
-            "check" => array(
+            "check" => [
                 "function" => "HttpContent",
                 "params" => $aOpts,
-            ),
-        )
+            ],
+        ]
     );
 }
 
@@ -252,56 +275,56 @@ if(count($aConfig['mirrorPackages'])){
 
         exec($sCmd, $sOut, $iRc);
         $oMonitor->addCheck(
-            array(
+            [
                 "name" => "mirror target $sHostKey",
                 "description" => "Sync target of generated packages",
                 "group" => "network",
                 "parent" => "read config file",
-                "check" => array(
+                "check" => [
                     "function" => "Simple",
-                    "params" => array(
+                    "params" => [
                         "result" => $iRc ? RESULT_ERROR : RESULT_OK,
                         "value" => "Command [$sCmd] returns with exitcode $iRc; output: ".implode(" ", $sOut),
-                    ),
-                ),
-            )
+                    ],
+                ],
+            ]
         );
     }
     
 }
-foreach(array(
+foreach([
     /*
-    array(
+    [
         'host'=>'gitlab.iml.unibe.ch',
         'port'=>22,
         'descr'=>'SSH port to Gitlab on gitlab.iml.unibe.ch'
-    ),
+    ],
      */
-    array(
+    [
         'host'=>'git-repo.iml.unibe.ch',
         'port'=>22,
         'descr'=>'SSH port to Gitlab on git-repo.iml.unibe.ch'
-    ),
-    array(
+    ],
+    [
         'host'=>'github.com',
         'port'=>22,
         'descr'=>'SSH port to Github'
-    ),
-) as $aDescr){
+    ],
+] as $aDescr){
     $oMonitor->addCheck(
-            array(
-                    "name" => 'port check '.$aDescr['host'].':'.$aDescr['port'],
-                    "description" => $aDescr['descr'],
-                    "group" => "cloud",
-                    "parent" => "read config file",
-                    "check" => array(
-                            "function" => "PortTcp",
-                            "params" => array(
-                                    "port"=>$aDescr['port'],
-                                    "host"=>$aDescr['host'],
-                            ),
-                    ),
-            )
+        [
+            "name" => 'port check '.$aDescr['host'].':'.$aDescr['port'],
+            "description" => $aDescr['descr'],
+            "group" => "cloud",
+            "parent" => "read config file",
+            "check" => [
+                "function" => "PortTcp",
+                "params" => [
+                    "port"=>$aDescr['port'],
+                    "host"=>$aDescr['host'],
+                ],
+            ],
+        ]
     );
 }
 
@@ -312,56 +335,56 @@ foreach(array(
 
 $sSqlitefile=$aConfig['dataDir'].'/database/logs.db';
 $oMonitor->addCheck(
-    array(
+    [
         "name" => "Sqlite DB for action logs",
         "description" => "Connect sqlite db ". basename($sSqlitefile),
         "parent" => "read config file",
-        "check" => array(
+        "check" => [
             "function" => "SqliteConnect",
-            "params" => array(
+            "params" => [
                 "db"=>$sSqlitefile
-            ),
-        ),
-    )
+            ],
+        ],
+    ]
 );
 
 // ----------------------------------------------------------------------
 // system stuff
 // ----------------------------------------------------------------------
 $oMonitor->addCheck(
-    array(
+    [
         "name" => "plugin Load",
         "description" => "current load",
         "parent" => false,
-        "check" => array(
+        "check" => [
             "function" => "Loadmeter",
-            "params" => array(
+            "params" => [
                 "warning" => 1.0,
                 "error" => 3,
-            ),
-        ),
+            ],
+        ],
         "worstresult" => RESULT_OK
-    )
+    ]
 );
 $oMonitor->addCheck(
-    array(
+    [
         "name" => "plugin ApacheProcesses",
         "description" => "Apache processes",
         "parent" => false,
-        "check" => array(
+        "check" => [
             "function" => "ApacheProcesses",
-            "params" => array(
+            "params" => [
                 "warning" => 50,
                 "error" => 75,
-            ),
-        ),
+            ],
+        ],
         "worstresult" => RESULT_OK
-    )
+    ]
 );
 
+// ----------------------------------------------------------------------
 
-// Gesamt-Ergebnis - ohne Param=aut. max. Wert nehmen
 $oMonitor->setResult();
-
-// Ausgabe
 $oMonitor->render();
+
+// ----------------------------------------------------------------------
diff --git a/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php b/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php
index 0c468221f0620aaf3a9b600fe3d3f1099511d2e7..01797f877e280adcc54f4d99812c930f8c18c112 100755
--- a/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php
+++ b/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php
@@ -163,6 +163,24 @@ $oMonitor->addCheck(
 // ----------------------------------------------------------------------
 include 'shared_check_ssl.php';
 
+
+$oMonitor->addCheck(
+    [
+        "name" => "plugin Load",
+        "description" => "current load",
+        "group" => 'monitor',
+        "parent" => false,
+        "check" => [
+            "function" => "Loadmeter",
+            "params" => [
+                "warning" => 1.0,
+                "error" => 3,
+            ],
+        ],
+        "worstresult" => RESULT_OK
+    ]
+);
+
 // ----------------------------------------------------------------------
 // plugin test
 // ----------------------------------------------------------------------
diff --git a/public_html/appmonitor/plugins/apps/inc_appcheck_end.php b/public_html/appmonitor/plugins/apps/inc_appcheck_end.php
new file mode 100644
index 0000000000000000000000000000000000000000..5f958628cffe7fb80936cf50bc333c5e032b39e6
--- /dev/null
+++ b/public_html/appmonitor/plugins/apps/inc_appcheck_end.php
@@ -0,0 +1,15 @@
+<?php
+/* ______________________________________________________________________
+ * 
+ * A P P M O N I T O R  ::  CLIENT - INCLUDE FOR APP CHECKS :: ON END
+ * ______________________________________________________________________
+ */
+
+// $bStandalone was set in inc_appcheck_start.php
+// send response if client was not initialized there
+if($bStandalone){
+    $oMonitor->setResult();
+    $oMonitor->render();
+}
+
+// ----------------------------------------------------------------------
diff --git a/public_html/appmonitor/plugins/apps/inc_appcheck_start.php b/public_html/appmonitor/plugins/apps/inc_appcheck_start.php
new file mode 100644
index 0000000000000000000000000000000000000000..93e17e635c07e66aa8b2680cfb39e8d5738e3472
--- /dev/null
+++ b/public_html/appmonitor/plugins/apps/inc_appcheck_start.php
@@ -0,0 +1,40 @@
+<?php
+/* ______________________________________________________________________
+ * 
+ * A P P M O N I T O R  ::  CLIENT - INCLUDE FOR APP CHECKS :: ON START
+ * ______________________________________________________________________
+ */
+
+// ----------------------------------------------------------------------
+// CHECK IF THE APPROOT IS SET
+// ----------------------------------------------------------------------
+
+if (!$sApproot) {
+    header('HTTP/1.0 503 Service Unavailable');
+    echo "<h1>503 Service Unavailable</h1>";
+    echo 'ERROR:'.PHP_EOL;
+    echo '$sApproot was not set. Define it before including the application check.'.PHP_EOL;
+    echo 'Set the base folder of your application installation.'.PHP_EOL;
+    echo PHP_EOL;
+    echo 'Example:'.PHP_EOL;
+    echo '$sApproot = $_SERVER[\'DOCUMENT_ROOT\'];'.PHP_EOL;
+    echo '$sApproot = $_SERVER[\'DOCUMENT_ROOT\'].\'/myapp\';'.PHP_EOL;
+    die();
+}
+
+// initialize client and set very basic metadata ... if needed
+$bStandalone=!(class_exists('appmonitor') && isset($oMonitor));
+if($bStandalone){
+    require_once(__DIR__.'/../../classes/appmonitor-client.class.php');
+    $oMonitor = new appmonitor();
+    $oMonitor->setWebsite('Wordpress Instance');
+
+    @include __DIR__.'/../../general_include.php';
+}
+
+// ----------------------------------------------------------------------
+// FUNCTIONS
+// ----------------------------------------------------------------------
+
+
+// ----------------------------------------------------------------------
diff --git a/public_html/appmonitor/plugins/apps/wordpress.php b/public_html/appmonitor/plugins/apps/wordpress.php
new file mode 100644
index 0000000000000000000000000000000000000000..711a15fcc7bbc64f564f95cd07c6e7e9cba9a643
--- /dev/null
+++ b/public_html/appmonitor/plugins/apps/wordpress.php
@@ -0,0 +1,101 @@
+<?php
+/* ______________________________________________________________________
+ * 
+ * WORK IN PROGRESS
+ * 
+ * A P P M O N I T O R  ::  CLIENT - CHECK
+ * ______________________________________________________________________
+ * 
+ * Check for a Wordpress instance.
+ * Blogsoftware https://wordpress.org/
+ * 
+ * It checks 
+ * - the write access to the config file
+ * - connect to mysql database (which is read from config)
+ * - ssl certificate (on https request only)
+ * 
+ * @author: <axel.hahn@unibe.ch>
+ * ----------------------------------------------------------------------
+ * 2018-11-07  v0.01
+ * 2019-05-24  v0.02  detect include or standalone mode
+ * 2019-05-24  v0.03  detect include or standalone mode
+ * 2024-07-31  v0.04  first version for wordpress check in plugins/apps/ 
+ */
+
+
+require 'inc_appcheck_start.php';
+
+// ----------------------------------------------------------------------
+// Read config items
+// ----------------------------------------------------------------------
+
+$sConfigfile = $sApproot . '/wp-config.php';
+if (!file_exists($sConfigfile)) {
+    header('HTTP/1.0 503 Service Unavailable');
+    die('ERROR: Config file [wp-config.php] was not found. Set a correct $sApproot pointing to wordpress install dir.');
+}
+
+require($sConfigfile);
+$aDb=[
+  'server'   => DB_HOST,
+  'username' => DB_USER,
+  'password' => DB_PASSWORD,
+  'database' => DB_NAME,
+  // 'port'     => ??,
+]; 
+
+// ----------------------------------------------------------------------
+// checks
+// ----------------------------------------------------------------------
+
+$oMonitor->addCheck(
+    [
+        "name" => "config file",
+        "description" => "The config file must be writable",
+        "check" => [
+            "function" => "File",
+            "params" => [
+                "filename" => $sConfigfile,
+                "file" => true,
+                "readable" => true,
+                "writable" => true,
+            ],
+        ],
+    ]
+);
+
+$oMonitor->addCheck(
+    [
+        "name" => "Mysql Connect",
+        "description" => "Connect mysql server " . $aDb['server'] . " as user " . $aDb['username'] . " to scheme " . $aDb['database'],
+        "parent" => "config file",
+        "check" => [
+            "function" => "MysqlConnect",
+            "params" => [
+                "server"   => $aDb['server'],
+                "user"     => $aDb['username'],
+                "password" => $aDb['password'],
+                "db"       => $aDb['database'],
+                // "port"     => $aDb['port'],
+            ],
+        ],
+    ]
+);
+
+if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']){
+    $oMonitor->addCheck(
+        [
+            "name" => "Certificate check",
+            "description" => "Check if SSL cert is valid and does not expire soon",
+            "check" => [
+                "function" => "Cert",
+            ],
+        ]
+    );
+}
+
+// ----------------------------------------------------------------------
+
+require 'inc_appcheck_end.php';
+
+// ----------------------------------------------------------------------
diff --git a/public_html/appmonitor/tests/test_plugins.php b/public_html/appmonitor/tests/test_plugins.php
new file mode 100644
index 0000000000000000000000000000000000000000..bd62fa4f6eb962052d989d27840f381d2493e6f6
--- /dev/null
+++ b/public_html/appmonitor/tests/test_plugins.php
@@ -0,0 +1,48 @@
+<?php
+/*
+    TEST CLIENT CHECKS
+*/
+
+// ----------------------------------------------------------------------
+// INIT
+$sApproot = str_replace('\\', '/', dirname(__DIR__));
+
+
+// require_once(__DIR__.'/../classes/client_all_in_one.php');
+// echo "OK: file client_all_in_one.php was loaded\n";
+
+require_once(__DIR__.'/../classes/appmonitor-client.class.php');
+
+$oMonitor = new appmonitor();
+
+$oMonitor->listChecks();
+
+
+echo "OK: class appmonitor was initialized\n";
+
+
+// ----------------------------------------------------------------------
+$oMonitor->addTag('monitoring');
+
+// ----------------------------------------------------------------------
+$oMonitor->addCheck(
+    [
+        "name" => "check config subdir",
+        "description" => "Check config target directory",
+        "check" => [
+            "function" => "File",
+            "params" => [
+                "filename" => $sApproot . "/server/config",
+                "dir" => true,
+                "writable" => true,
+            ],
+        ],
+    ]
+);
+echo "OK: the plugin File check was added.\n";
+
+// ----------------------------------------------------------------------
+$oMonitor->setResult();
+echo "OK: setResult() was executed.\n";
+
+// ----------------------------------------------------------------------
diff --git a/public_html/check-config.php b/public_html/check-config.php
index e66c1f4f3002eaad0ce37295cb508b7223f683a3..2b1de42cee481b48ee57a218d71c86c78c8f1591 100644
--- a/public_html/check-config.php
+++ b/public_html/check-config.php
@@ -1,3 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+    body{font-family: arial; background: #f8f8f8; color:#345;}
+    .box{border: 2px solid rgba(0,0,0,0.2); padding: 1em; margin-bottom: 1em;}
+    .ok{color:#383; background:#cfd;}
+    .error{color:#833; background:#fdd;}
+</style>
+</head>
+<body>
+
+<h1>CI Server :: Config Check</h1>
+
 <?php
 /* ######################################################################
 
@@ -10,120 +24,137 @@
   ###################################################################### */
 
 
-
 // ----------------------------------------------------------------------
 // functions
 // ----------------------------------------------------------------------
 
 /**
- * get html code to draw a colored box
+ * Get html code to draw a colored box
+ * 
  * @param string  $s       message
  * @param string  $sClass  css class; one of ok|info|error
  * @return string
  */
-function drawbox($s, $sClass) {
-    return '<div class="box ' . $sClass . '">' . $s . '</div>';
+function drawbox(string $s, string $sClass): string
+{
+    return "<div class=\"box $sClass\">$s</div>";
 }
 
 /**
- * show an alert box with error message
- * @param type $s
- * @return type
+ * Show an alert box with error message
+ * 
+ * @param string $s  message to show
+ * @return string
  */
-function showerror($s) {
+function showerror($s)
+{
     return drawbox($s, 'error');
 }
 
 /**
- * show green box with ok message
- * @param type $s
- * @return type
+ * Show green box with ok message
+ * 
+ * @param string $s  message to show
+ * @return string
  */
-function showok($s) {
+function showok($s)
+{
     return drawbox($s, 'ok');
 }
 
 /**
- * check a directory if it exists and is writtable
+ * Check a directory if it exists and is writtable
+ * 
  * @global array  $aErrors  found errors
+ * 
  * @param string  $sDir     directory to check
- * @param string  $sKey     key in config (for output only)
- * @return boolean
+ * @param string  $sKey     key in config where this directory is defined (for error output only)
+ * @return string
  */
-function checkdir($sDir, $sKey = false) {
+function checkdir(string $sDir, string $sKey = ''): string
+{
     global $aErrors;
+    $sReturn = '';
 
-    echo 'check directory [' . $sDir . '] ';
+    $sReturn.="Check directory [$sDir] ";
     if (!is_dir($sDir)) {
-        echo '<span class="error">does not exist</span><br>';
-        $aErrors[] = "* \$aConfig['$sKey'] points to a non existing directory (" . $sDir . ").\n";
-        return false;
+        $sReturn.='<span class="error">does not exist</span><br>';
+        $aErrors[] = "* \$aConfig['$sKey'] points to a non existing directory ($sDir).\n";
     } else {
         if (!is_writable($sDir)) {
-            echo '<span class="error">not writable</span><br>';
-            $aErrors[] = "* \$aConfig['$sKey'] = " . $sDir . " is NOT writable.\n";
-            return false;
+            $sReturn.='<span class="error">not writable</span><br>';
+            $aErrors[] = "* \$aConfig['$sKey'] = $sDir is NOT writable.\n";
         } else {
-            echo '<span class="ok">OK</span> exists and is writable<br>';
-            return true;
+            $sReturn.='<span class="ok">OK</span> exists and is writable<br>';
         }
     }
-    return false;
+    return $sReturn;
 }
 
 /**
- * check if a php module was found
+ * Check if a php module was found
+ * 
  * @global array  $aErrors  found errors
+ * 
  * @param  array  $aRequiredMods  array with needed php modules; default: empty = list all current php modules
- * @return boolean
+ * @return string
  */
-function checkModule($aRequiredMods=array()){
+function checkModule($aRequiredMods = []): string
+{
     global $aErrors;
-    $sReturn='';
-    
-    $aAllMods=get_loaded_extensions(false);
+    $sReturn = '';
+
+    $aAllMods = get_loaded_extensions(false);
     asort($aAllMods);
-    if(!count($aRequiredMods)){
-        echo '<strong>List of all installes php modules</strong><br>';
-        foreach($aAllMods as $sMod){
-            echo $sMod.'<br>';
+    if (!count($aRequiredMods)) {
+        $sReturn.= '<strong>List of all installes php modules</strong><br>';
+        foreach ($aAllMods as $sMod) {
+            $sReturn.= "$sMod<br>";
         }
     } else {
-        foreach($aRequiredMods as $sMod){
-            echo $sMod.' - ';
-            if(!array_search($sMod, $aAllMods)===false){
-                echo  '<span class="ok">OK</span> installed';
+        foreach ($aRequiredMods as $sMod) {
+            $sReturn.="$sMod - ";
+            if (!array_search($sMod, $aAllMods) === false) {
+                $sReturn.='<span class="ok">OK</span> installed';
             } else {
-                echo '<span class="error">does not exist</span>';
+                $sReturn.='<span class="error">does not exist</span>';
                 $aErrors[] = "* php module $sMod was not found.\n";
             }
-            echo '<br>';
+            $sReturn.='<br>';
         }
     }
-    return true;   
+    return $sReturn;
 }
 
 /**
- * check if a command can be executed
+ * Check if a command can be executed
+ * 
  * @global array  $aErrors  found errors
+ * 
  * @param  array  $aCommands  array with key=command + value=description
- * @return boolean
+ * @return string
  */
-function checkCommands($aCommands){
+function checkCommands(array $aCommands): string
+{
     global $aErrors;
-    foreach ($aCommands as $sCommand=>$sDescription){
-            echo 'command ['.$sCommand.'] ('.$sDescription.') - ';
-            system("$sCommand", $iReturn);
-            
-            if($iReturn===0){
-                echo  '<span class="ok">OK</span> ';
-            } else {
-                echo '<span class="error">does not exist</span>';
-                $aErrors[] = "* command $sCommand was not found or failed.\n";
-            }
-            echo '<br>';
+    $sReturn = '';
+
+    foreach ($aCommands as $sCommand => $sDescription) {
+        $sReturn.='command [' . $sCommand . '] (' . $sDescription . ') - ';
+
+        $aOut=[];
+        exec("$sCommand 2>&1", $aOut, $iReturn) .  " ";
+        $sReturn.= implode("\n", $aOut) . "\n";
+
+        if ($iReturn === 0) {
+            $sReturn.='<span class="ok">OK</span> ';
+        } else {
+            $sReturn.='<span class="error">does not exist</span>';
+            $aErrors[] = "* command $sCommand was not found or failed.\n";
+        }
+        $sReturn.='<br>';
     }
-    return true;
+    return $sReturn;
 }
 
 // ----------------------------------------------------------------------
@@ -132,113 +163,94 @@ function checkCommands($aCommands){
 // 
 // ----------------------------------------------------------------------
 
-echo '
-<style>
-    body{font-family: arial; background: #f8f8f8; color:#345;}
-    .box{border: 2px solid rgba(0,0,0,0.2); padding: 1em; margin-bottom: 1em;}
-    .ok{color:#383; background:#cfd;}
-    .error{color:#833; background:#fdd;}
-</style>
-<h1>Config Checker</h1>
-';
+$sOut="";
 
-$aErrors = array();
+$aErrors = [];
 include("../config/inc_projects_config.php");
 if (!isset($aConfig) || !is_array($aConfig)) {
     // echo showerror("\$aConfig does not exist. The config was not included before including " . __FILE__ . " in the request/ script.");
     $aErrors[] = "* \$aConfig does not exist. The config was not included before including " . __FILE__ . " in the request/ script.";
 } else {
-    
+
     // ----------------------------------------------------------------------
-    echo '<h2>Check keys with directories</h2>';
-    foreach (array(
-        // 'appRootDir',
-        'configDir',
-        'workDir',
-        'dataDir',
-        'buildDir',
-        'buildDefaultsDir',
-        'packageDir',
-        'archiveDir',
-        'tmpDir',
-    ) as $sKey) {
-        checkdir($aConfig[$sKey], $sKey);
+    $sOut.='<h2>Check keys with directories</h2>';
+    foreach ([ // 'appRootDir',
+        'configDir', 'workDir', 'dataDir', 'buildDir', 'buildDefaultsDir', 'packageDir', 'archiveDir', 'tmpDir', ] as $sKey) {
+        $sOut.=checkdir($aConfig[$sKey], $sKey);
     }
-    
-    echo '<h2>Check subdirs of key dataDir</h2>';
-    foreach (array(
-            $aConfig['dataDir'] . '/database',
-            $aConfig['dataDir'] . '/projects',
-            $aConfig['dataDir'] . '/sshkeys',
-        ) as $sDir2Check) {
-        checkdir($sDir2Check, 'dataDir + [subdir]');
+
+    $sOut.='<h2>Check subdirs of key dataDir</h2>';
+    foreach ([$aConfig['dataDir'] . '/database', $aConfig['dataDir'] . '/projects', $aConfig['dataDir'] . '/sshkeys', ] as $sDir2Check) {
+        $sOut.=checkdir($sDir2Check, 'dataDir + [subdir]');
     }
-    
+
     // ----------------------------------------------------------------------
-    echo '<h2>Check keys</h2>';
+    $sOut.='<h2>Check keys</h2>';
     // check required keys in the config
-    foreach (array(
-     'auth',
-     'build',
-     'lang',
-     'phases',
-     'projects',
-    ) as $sKey) {
-        echo "Key [$sKey] ";
+    foreach (['auth', 'build', 'lang', 'phases', 'projects', ] as $sKey) {
+        $sOut.="Key [$sKey] ";
         if (!array_key_exists($sKey, $aConfig)) {
-            echo '<span class="error">failed</span> missing key ['.$sKey.'] in config<br>';
+            $sOut.="<span class=\"error\">failed</span> missing key [$sKey] in config<br>";
             $aErrors[] = "* missing key [$sKey] in config\n";
         } else {
-            echo '<span class="ok">OK</span> exists<br>';
+            $sOut.='<span class="ok">OK</span> exists<br>';
         }
     }
 }
 
-echo '<h2>Check required PHP modules</h2>';
-echo 'PHP version: '.PHP_VERSION.'<br>'
-        . '<br>';
-checkModule(array(
-    'PDO','curl', 'json', 'ldap', 'pdo_sqlite'
-));
+$sOut.='<h2>Check required PHP modules</h2>';
+$sOut.='PHP version: ' . PHP_VERSION . '<br>'
+    . '<br>';
+$sOut.=checkModule([
+    'PDO',
+    'curl',
+    'json',
+    'ldap',
+    'pdo_sqlite'
+]);
 
-echo '<h2>Check executable commands</h2>'
+$sOut.='<h2>Check executable commands</h2>'
     . '<p>remark: if the apache user has no login shell then all commands will fail.</p>';
-echo '<h3>basic tools</h3>';
-checkCommands(array(
-    'which bash'=>'shell interpreter to execute hook scripts', 
-    'which cat'=>'show content of files', 
-    'which cp'=>'copy files', 
-    'which ln'=>'create softlinks for version handling', 
-    'which ls'=>'list files', 
-    'which mkdir'=>'create working directories', 
-    'which mv'=>'move files', 
-    'which ps'=>'process list',
-    'which rm'=>'remove files and directories', 
-));
-echo '<h3>more tools</h3>';
-checkCommands(array(
-    'which ssh'=>'connect with ssh for remote execution',
-    'which rsync'=>'sync packages to puppet master',
-    'which git'=>'connect to git repositories',
-));
-
-echo '<h3>tools for IML projects</h3>';
-checkCommands(array(
-    'which uglifyjs'=>'compress js',
-    'which yui-compressor'=>'compress js, css',
-    'which nodejs'=>'Nodejs - Javascript v8',
-    'which yarn'=>'nodejs package manager',
-));
+$sOut.='<h3>basic tools</h3>';
+$sOut.=checkCommands([
+    'which bash' => 'shell interpreter to execute hook scripts',
+    'which cat' => 'show content of files',
+    'which cp' => 'copy files',
+    'which ln' => 'create softlinks for version handling',
+    'which ls' => 'list files',
+    'which mkdir' => 'create working directories',
+    'which mv' => 'move files',
+    'which ps' => 'process list',
+    'which rm' => 'remove files and directories',
+]);
+$sOut.='<h3>More tools</h3>';
+$sOut.=checkCommands([
+    'which ssh' => 'connect with ssh for remote execution',
+    'which rsync' => 'sync packages to puppet master',
+    'which git' => 'connect to git repositories',
+]);
+
+$sOut.='<h3>tools for IML projects</h3>';
+$sOut.=checkCommands([
+    'which uglifyjs' => 'compress js',
+    'which yui-compressor' => 'compress js, css',
+    'which nodejs' => 'Nodejs - Javascript v8',
+    'which yarn' => 'nodejs package manager',
+]);
 
 
 
-echo '<h2>Result</h2>';
 
 if (count($aErrors)) {
-    echo showerror("FATAL ERROR in config.<br>" . implode("<br>\n", $aErrors));
+    echo showerror("ERRORS were found:<br>" . implode("<br>\n", $aErrors));
 } else {
     echo showok('OK, check was successfully finished.');
 }
-
+echo '<h2>Result</h2>';
+echo $sOut;
 // do not enable - it shows passwords...
 // echo 'DEBUG: <pre>' . print_r($aConfig, 1) . '</pre>';
+
+?>
+</body>
+</html>
diff --git a/public_html/deployment/classes/actionlog.class.php b/public_html/deployment/classes/actionlog.class.php
index ac16f79425b963a1376287d9e32d5aed196839a1..021dfaa1af0d851f6af6b7283874915277d3bd2f 100644
--- a/public_html/deployment/classes/actionlog.class.php
+++ b/public_html/deployment/classes/actionlog.class.php
@@ -5,16 +5,46 @@ require_once 'user.class.php';
  * class to log all project actions, ie. build, deploy etc.
  *
  * @author hahn
+ * 
+ * 2024-08-23  v1.1  Axel Hahn  php8 only; added variable types; short array syntax; remove glyphicons
  */
 class Actionlog
 {
 
-    private $_dbfile = '';
-    private $_aLoglevels = array("info", "warning", "error", "success"); // array of valid loglevels
-    private $_sIP = false;
-    private $_sUser = false;
-    private $_sProject = false;
-    private $_sCreate = '
+    /**
+     * database file
+     * @var string
+     */
+    private string $_dbfile = '';
+    /**
+     * List of allowed loglevels
+     * @var array
+     */
+    private array $_aLoglevels = ["info", "warning", "error", "success"]; // array of valid loglevels
+
+    /**
+     * Ip address of the client
+     * @var string
+     */
+    private string $_sIP = '';
+
+    /**
+     * Authenticated user on the client
+     * @var string
+     */
+    private string $_sUser = '';
+
+    /**
+     * Current project
+     * @var string
+     */
+    private string $_sProject = '';
+
+    /**
+     * Create statement for the database
+     * @var string
+     */
+    private string $_sCreate = '
         CREATE TABLE "logs" (
           `id` INTEGER PRIMARY KEY  AUTOINCREMENT  NOT NULL  UNIQUE ,
           `time` DATETIME,
@@ -39,13 +69,14 @@ class Actionlog
           `message` TEXT
         )';
         */
+
     /**
-     * constructor - sets internal environment variables and checks existence 
+     * Constructor - sets internal environment variables and checks existence 
      * of the database
      * @global array $aConfig    settings
      * @param  string $sProject  project ID
      */
-    public function __construct($sProject = false)
+    public function __construct(string $sProject = '')
     {
         global $aConfig;
         if (!isset($aConfig["appRootDir"])) {
@@ -71,20 +102,22 @@ class Actionlog
     }
 
     /**
-     * create sqlite database - called in constructor if the file does not exist
+     * Create sqlite database - called in constructor if the file does not exist
      */
-    private function _createDb()
+    private function _createDb(): bool|PDOstatement
     {
         echo "try to create file $this->_dbfile ...<br>\n";
         return $this->_makeQuery($this->_sCreate);
     }
 
     /**
-     * execute a sql statement
+     * Execute a sql statement and get the result
+     * TODO: support prepared statements
+     * 
      * @param string $sSql sql statement
-     * @return database object
+     * @return bool|PDO statement
      */
-    private function _makeQuery($sSql)
+    private function _makeQuery(string $sSql): bool|PDOstatement
     {
         // $this->_log(__FUNCTION__."($sSql)");
         // echo "<pre>".htmlentities($sSql)."</pre>";
@@ -100,16 +133,18 @@ class Actionlog
     }
 
     /**
-     * add a log message
+     * Add a log message
+     * TODO: supoport prepared statements
+     * 
      * @param string $sMessage   message
      * @param string $sAction    project action; i.e. build, deploy, ...
      * @param string $sLoglevel  loglevel
-     * @return type
+     * @return bool|PDOstatement
      */
-    public function add($sMessage, $sAction = "", $sLoglevel = "info", $sTimeStart = false)
+    public function add(string $sMessage, string $sAction = "", string $sLoglevel = "info" /*, $sTimeStart = false */): bool|PDOstatement
     {
         if (array_search($sLoglevel, $this->_aLoglevels) === false) {
-            die(__class__ . ": loglevel $sLoglevel is invalid");
+            die(__CLASS__ . ": loglevel $sLoglevel is invalid");
         }
         $sql = "INSERT INTO `logs` (`time`, `loglevel`, `ip`, `user`, `project` ,`action`, `message`)
           VALUES(
@@ -143,18 +178,18 @@ class Actionlog
     }
 
     /**
-     * helper function to remove chars in a string
+     * Helper function to remove chars in a string
      * @param string  $sVal      user value
      * @param string  $sOKChars  good chars to keep
      * @return string
      */
-    private function _filterAllowedChars($sVal, $sOKChars)
+    private function _filterAllowedChars(string $sVal, string $sOKChars): string
     {
         return preg_replace('/[^' . $sOKChars . ']/i', '', $sVal);
     }
 
     /**
-     * get log data
+     * Get log data
      * @param array $aFilter with the following keys:
      *   'project' - filter by project; will be mixed with where (see next key)
      *   'from   ' - time greater equal; time as string i.e. "2020-06-24" or "2020-06-24 11:00:00"
@@ -163,15 +198,15 @@ class Actionlog
      *   'limit'   - limit clausel - part behind "LIMIT "
      * @return array
      */
-    public function getLogs($aFilter = array())
+    public function getLogs(array $aFilter = []): array
     {
         // var_dump(R::findAll( 'log' ));
-        $aReturn = array();
+        $aReturn = [];
 
         $sSql = 'SELECT `id`,`time`,`loglevel`,`ip`,`user`,`project`,`action`,`message`  from logs ';
         $sWhere = false;
 
-        $aWhere = array();
+        $aWhere = [];
         if (isset($aFilter["project"]) && $aFilter["project"]) {
             $aWhere[] = '`project`="' . $this->_filterAllowedChars($aFilter["project"], '[a-z0-9\-\_]') . '"';
         }
@@ -203,14 +238,14 @@ class Actionlog
     }
 
     /**
-     * render html code for a table with logs. The filter will be sent to
+     * Get html code for a table with logs. The filter will be sent to
      * getLogs method.
      * @param array $aFilter with the following keys:
      *   'project' - filter by project; will be mixed with where (see next key)
      *   'limit' - limit clausel - part behind "LIMIT "
      * @return string
      */
-    public function renderLogs($aFilter = array(), $bIsFullsearch = false)
+    public function renderLogs(array $aFilter = [], bool $bIsFullsearch = false): string
     {
         $sReturn = '';
 
@@ -220,35 +255,36 @@ class Actionlog
 
         $bWasShown = true;
         require_once 'formgen.class.php';
+        $oHtml = new htmlguielements();
 
         // values for dropdowns - limit of lines; time
 
-        $aLimits = array(
-            '20' => array('label' => 20),
-            '50' => array('label' => 50),
-            '100' => array('label' => 100),
-            '' => array('label' => t("all")),
-        );
-        $aTimes = array(
-            date("Y-m-d", date("U"))                => array('label' => t("class-actionlog-time-today")),
-            date("Y-m-d", date("U") - 60 * 60 * 24 * 1)   => array('label' => t("class-actionlog-time-since-yesterday")),
-            date("Y-m-d", date("U") - 60 * 60 * 24 * 7)   => array('label' => t("class-actionlog-time-for-1-week")),
-            date("Y-m-d", date("U") - 60 * 60 * 24 * 7 * 2) => array('label' => sprintf(t("class-actionlog-time-for-n-weeks"), "2")),
-            date("Y-m-d", date("U") - 60 * 60 * 24 * 7 * 4) => array('label' => sprintf(t("class-actionlog-time-for-n-weeks"), "4")),
-            '' => array('label' => t("all")),
-        );
-
-        $aForms = array(
-            'filter' => array(
-                'meta' => array(
+        $aLimits = [
+            '20' => ['label' => 20],
+            '50' => ['label' => 50],
+            '100' => ['label' => 100],
+            '' => ['label' => t("all")],
+        ];
+        $aTimes = [
+            date("Y-m-d", date("U")) => ['label' => t("class-actionlog-time-today")],
+            date("Y-m-d", date("U") - 60 * 60 * 24 * 1) => ['label' => t("class-actionlog-time-since-yesterday")],
+            date("Y-m-d", date("U") - 60 * 60 * 24 * 7) => ['label' => t("class-actionlog-time-for-1-week")],
+            date("Y-m-d", date("U") - 60 * 60 * 24 * 7 * 2) => ['label' => sprintf(t("class-actionlog-time-for-n-weeks"), "2")],
+            date("Y-m-d", date("U") - 60 * 60 * 24 * 7 * 4) => ['label' => sprintf(t("class-actionlog-time-for-n-weeks"), "4")],
+            '' => ['label' => t("all")],
+        ];
+
+        $aForms = [
+            'filter' => [
+                'meta' => [
                     'method' => 'GET',
                     'action' => '?',
                     'class' => 'form-inline',
-                ),
-                'validate' => array(),
-                'form' => array(),
-            )
-        );
+                ],
+                'validate' => [],
+                'form' => [],
+            ]
+        ];
 
         // generate filter for log table
         // $bIsFullsearch: true = show all inputs; false: no interactive search
@@ -257,83 +293,83 @@ class Actionlog
 
             // --- list of all projects in log
             $sSql = 'SELECT DISTINCT(project) from `logs` order by project asc';
-            $aForms["filter"]["form"]['selectproject'] = array(
+            $aForms["filter"]["form"]['selectproject'] = [
                 'type' => 'select',
                 'name' => 'selectproject',
-                'label' => '<i class="glyphicon glyphicon-tag"></i> ' . t('project'),
+                'label' => $oHtml->getIcon('project') . '&nbsp;' . t('project'),
                 'class' => 'span2',
                 'onchange' => 'updateActionlog();',
                 'inline' => true,
-            );
-            $aForms["filter"]["form"]['selectproject']['options'][''] = array('label' => t("all"));
+            ];
+            $aForms["filter"]["form"]['selectproject']['options'][''] = ['label' => t("all")];
             foreach ($this->_makeQuery($sSql) as $row) {
                 if ($row[0]) {
-                    $aForms["filter"]["form"]['selectproject']['options'][$row[0]] = array('label' => $row[0]);
+                    $aForms["filter"]["form"]['selectproject']['options'][$row[0]] = ['label' => $row[0]];
                 }
             }
-            $aForms["filter"]["form"]['selectfrom'] = array(
+            $aForms["filter"]["form"]['selectfrom'] = [
                 'type' => 'select',
                 'name' => 'selectWheretime',
-                'label' => '<i class="glyphicon glyphicon-calendar"></i> ' . t("class-actionlog-time"),
+                'label' => $oHtml->getIcon('calendar') . '&nbsp;' . t("class-actionlog-time"),
                 'class' => 'span2',
                 'onchange' => 'updateActionlog();',
                 'options' => $aTimes,
                 'inline' => true,
-            );
-            $aForms["filter"]["form"]['selectlimit'] = array(
+            ];
+            $aForms["filter"]["form"]['selectlimit'] = [
                 'type' => 'select',
                 'name' => 'selectlimit',
-                'label' => '<i class="glyphicon glyphicon-list"></i> ' . t("class-actionlog-count"),
+                'label' => $oHtml->getIcon('list') . '&nbsp;' . t("class-actionlog-count"),
                 'class' => 'span1',
                 'onchange' => 'updateActionlog();',
                 'options' => $aLimits,
                 'inline' => true,
-            );
+            ];
 
             // force a line break
-            $aForms["filter"]["form"]['line'] = array(
+            $aForms["filter"]["form"]['line'] = [
                 'type' => 'markup',
                 'value' => '<div class="col-sm-12"></div>',
-            );
+            ];
         } else {
 
             // write filters as hidden fields
             if (isset($aFilter["project"])) {
-                $aForms["filter"]["form"]['selectproject'] = array(
+                $aForms["filter"]["form"]['selectproject'] = [
                     'type' => 'hidden',
                     'value' => $aFilter["project"]
-                );
+                ];
             }
             if (isset($aFilter["limit"])) {
-                $aForms["filter"]["form"]['selectlimit'] = array(
+                $aForms["filter"]["form"]['selectlimit'] = [
                     'type' => 'hidden',
                     'value' => $aFilter["limit"]
-                );
+                ];
             }
         }
-        $aForms["filter"]["form"]['efilterlogs'] = array(
+        $aForms["filter"]["form"]['efilterlogs'] = [
             'type' => 'text',
             'name' => 'efilterlogs',
-            'label' => '<i class="glyphicon glyphicon-filter"></i>' . t("overview-textsearch"),
+            'label' => $oHtml->getIcon('filter') . '&nbsp;' . t("overview-textsearch"),
             'inline' => true,
             'onkeyup' => 'filterLogTable();',
             'onkeypress' => 'filterLogTable();',
             'onchange' => 'filterLogTable();',
             //'size' => 20,
-        );
+        ];
         if (!$bIsFullsearch) {
-            $aForms["filter"]["form"]['btnalllogs'] = array(
+            $aForms["filter"]["form"]['btnalllogs'] = [
                 'type' => 'button',
                 'value' => t('show all'),
                 'class' => 'btn btn-secondary btnlogs',
                 'href' => '/deployment/all/setup/actionlog/',
                 'onclick' => 'location.href=\'/deployment/all/setup/actionlog/\'; return false;',
-            );
+            ];
         } else {
-            // $aForms["filter"]["form"]['closediv'] = array(
+            // $aForms["filter"]["form"]['closediv'] = [
             //     'type' => 'markup',
             //     'value' => '</div><!-- closediv -->',
-            // );
+            // ];
 
         }
 
@@ -354,7 +390,7 @@ class Actionlog
                     <button onclick="setTimelineVisibility(\'block\');"  id="btnShowTimeline" class="btn btn-default btnTimeline"><i class="fa-solid fa-chevron-right"></i> </button>
                     <button onclick="setTimelineVisibility(\'none\');"   id="btnHideTimeline" class="btn btn-default btnTimeline"><i class="fa-solid fa-chevron-down"></i> </button>
                     &nbsp; ' . t("class-actionlog-timeline") . (isset($aFilter["project"]) ? ' [' . $aFilter["project"] . '] ' : '')
-                    .'<div id="divTimeline"></div>
+            . '<div id="divTimeline"></div>
                 </div>
                 <script>
                     var sMsgNolog="' . t("class-actionlog-nolog") . '";
diff --git a/public_html/deployment/classes/base.class.php b/public_html/deployment/classes/base.class.php
index cefb6605b88a9ef59a2eb029e0b16410a45f4bce..fdaa3789f7c0db8a0ad234cfb97dbde10ee51067 100644
--- a/public_html/deployment/classes/base.class.php
+++ b/public_html/deployment/classes/base.class.php
@@ -7,14 +7,15 @@ require_once 'user.class.php';
  *
  * @author hahn
  */
-class base {
+class base
+{
 
     /**
-     * logged in user
-     * @var object
+     * logged in user as user object
+     * @var user
      */
-    var $oUser=false;
-    
+    public user $oUser;
+
     /**
      * init user with optional given user
      * @param type $sUser
@@ -22,5 +23,5 @@ class base {
         $this->oUser=new user();
     }
      */
-    
+
 }
diff --git a/public_html/deployment/classes/build.interface.php b/public_html/deployment/classes/build.interface.php
index 9c9abf36cf15b5c69b65699c9d8829eaea719b4f..f9c0e6017076bbed54a2e7596ffebc36e0dfe054 100644
--- a/public_html/deployment/classes/build.interface.php
+++ b/public_html/deployment/classes/build.interface.php
@@ -4,20 +4,21 @@
  * 
  * @author hahn
  */
-interface iBuildplugin {
-    
+interface iBuildplugin
+{
+
     /**
-     * get an array of commands to check requirements 
+     * Get an array of commands to check requirements 
      * if the plugin is able to work
      * @return array
      */
-    public function checkRequirements();
+    public function checkRequirements(): array;
 
     /**
-     * get an array with shell commands to execute
+     * Get an array with shell commands to execute
      * @return array
      */
-    public function getBuildCommands();
-    
+    public function getBuildCommands(): array;
+
 }
 
diff --git a/public_html/deployment/classes/build_base.class.php b/public_html/deployment/classes/build_base.class.php
index 723deb290e476fcf7cb8b1f746a2838277fd084f..29945461c3ac0c2949fd3ba6f688890cc28320a8 100644
--- a/public_html/deployment/classes/build_base.class.php
+++ b/public_html/deployment/classes/build_base.class.php
@@ -6,17 +6,47 @@ require_once 'build.interface.php';
  * see deployment/plugins/build/*
  * 
  * @author axel
+ * 
+ * 2024-08-23  v1.1  Axel Hahn  php8 only; added variable types; short array syntax
  */
-class build_base implements iBuildplugin{
-    
-    protected $_sBuildDir = false;
-    protected $_sOutfile = false;
-    
-    protected $_sPluginId = false;
-    protected $_aPlugininfos = false;
+class build_base implements iBuildplugin
+{
 
-    protected $_sLang = "en-en";
-    protected $_aLang = [];
+    /**
+     * path of the build directory
+     * @var string
+     */
+    protected string $_sBuildDir = '';
+
+    /**
+     * outputfile during build
+     * @var string
+     */
+    protected string $_sOutfile = '';
+
+    /**
+     * id of the plugin
+     * @var string
+     */
+    protected string $_sPluginId = '';
+
+    /**
+     * array with plugin infos
+     * @var array
+     */
+    protected array $_aPlugininfos = [];
+
+    /**
+     * language of ui; default is "en-en"
+     * @var string
+     */
+    protected string $_sLang = "en-en";
+
+    /**
+     * array of language texts
+     * @var array
+     */
+    protected array $_aLang = [];
 
 
     // ---------------------------------------------------------------
@@ -24,58 +54,59 @@ class build_base implements iBuildplugin{
     // ---------------------------------------------------------------
 
     /**
-     * initialize build plugin
+     * Constructor
+     * Initialize build plugin
      * @param array $aParams  hash with those possible keys
      *                  lang         string   language, i.e. 'de'
      *                  phase        string   name of phase in a project
      *                  globalcfg    array    given global config $aConfig
      *                  projectcfg   array    project config to generate config 
      *                                        for project and all phases
-     * @return boolean
      */
-    public function __construct($aParams) {
-        
+    public function __construct(array $aParams)
+    {
+
         // set current plugin id - taken from plugin directory name above
-        $oReflection=new ReflectionClass($this);
-        $this->_sPluginId=basename(dirname($oReflection->getFileName()));
-   
+        $oReflection = new ReflectionClass($this);
+        $this->_sPluginId = basename(dirname($oReflection->getFileName()));
+
         // ----- init language
-        if (isset($aParams['lang'])){
+        if (isset($aParams['lang'])) {
             $this->setLang($aParams['lang']);
         } else {
             $this->setLang();
         }
-        if (isset($aParams['workdir'])){
+        if (isset($aParams['workdir'])) {
             $this->setWorkdir($aParams['workdir']);
         }
-        if (isset($aParams['outfile'])){
+        if (isset($aParams['outfile'])) {
             $this->setOutfile($aParams['outfile']);
         }
 
-        return true;
     }
-    
+
     // ---------------------------------------------------------------
     // LANGUAGE TEXTS
     // ---------------------------------------------------------------
-    
+
     /**
-     * get a translated text from lang_XX.json in plugin dir;
+     * Get a translated text from lang_XX.json in plugin dir;
      * If the key is missed it returns "[KEY :: LANG]"
      * 
      * @see setLang()
      * @param string $sKey  key to find in lang file
      * @return string
      */
-    protected function _t($sKey){
+    protected function _t(string $sKey): string
+    {
         return (isset($this->_aLang[$sKey]) && $this->_aLang[$sKey])
-                ? $this->_aLang[$sKey]
-                : "[ $sKey :: $this->_sLang ]"
+            ? $this->_aLang[$sKey]
+            : "[ $sKey :: $this->_sLang ]"
         ;
     }
 
     /**
-     * set language for output of formdata and other texts.
+     * Set language for output of formdata and other texts.
      * This method loads the language file into a hash. The output of 
      * translated texts can be done with $this->_t("your_key")
      * 
@@ -83,12 +114,13 @@ class build_base implements iBuildplugin{
      * @param string   $sLang  language code, i.e. "de"
      * @return boolean
      */
-    public function setLang($sLang=false){
-        $this->_sLang=$sLang ? $sLang : $this->_sLang;
-        
-        $oReflection=new ReflectionClass($this);
-        $sFile=dirname($oReflection->getFileName()) . '/lang_'.$this->_sLang.'.json';
-        $this->_aLang=(file_exists($sFile)) ? json_decode(file_get_contents($sFile), 1) : $this->_aLang;
+    public function setLang(string $sLang = ''): bool
+    {
+        $this->_sLang = $sLang ? $sLang : $this->_sLang;
+
+        $oReflection = new ReflectionClass($this);
+        $sFile = dirname($oReflection->getFileName()) . '/lang_' . $this->_sLang . '.json';
+        $this->_aLang = (file_exists($sFile)) ? json_decode(file_get_contents($sFile), 1) : $this->_aLang;
         return true;
     }
     // ---------------------------------------------------------------
@@ -97,123 +129,145 @@ class build_base implements iBuildplugin{
 
 
     /**
-     * set build dir with sources
+     * Set build dir with sources
      * @param  string  $sBuildDir     full path of the build directory
-     * @return array
+     * @return bool
      */
-    public function setWorkdir($sBuildDir){
-        return $this->_sBuildDir=$sBuildDir ? $sBuildDir : $this->_sBuildDir;
+    public function setWorkdir(string $sBuildDir): bool
+    {
+        $this->_sBuildDir = $sBuildDir ? $sBuildDir : $this->_sBuildDir;
+        return true;
     }
 
     /**
-     * set outfile name
+     * Set outfile name
      * @param  string  $sOutFilename  filename for output (without extension)
-     * @return array
+     * @return bool
      */
-    public function setOutfile($sOutFilename){
-        return $this->_sOutfile=$sOutFilename ? $sOutFilename : $this->_sOutfile;
+    public function setOutfile(string $sOutFilename)
+    {
+        $this->_sOutfile = $sOutFilename ? $sOutFilename : $this->_sOutfile;
+        return true;
     }
 
     // ---------------------------------------------------------------
     // GETTER
     // ---------------------------------------------------------------
-    
+
     /**
-     * check requirements if the plugin could work
+     * Get an array with shell commands to check requirements if the plugin
+     * can work
+     * 
      * @return array
      */
-    public function checkRequirements() {
+    public function checkRequirements(): array
+    {
         return [
-            'echo "ERROR: The method checkRequirements() was not implemented in the build plugin ['.$this->getId().']"',
+            'echo "ERROR: The method checkRequirements() was not implemented in the build plugin [' . $this->getId() . ']"',
             'exit 1'
-            ];
+        ];
     }
 
     /**
-     * get an array with shell commands to execute
+     * Get an array with shell commands to execute
      * @return array
      */
-    public function getBuildCommands(){
+    public function getBuildCommands(): array
+    {
         return [
-            'echo "ERROR: The method getBuildCommamds() was not implemented in the build plugin ['.$this->getId().']"',
+            'echo "ERROR: The method getBuildCommamds() was not implemented in the build plugin [' . $this->getId() . ']"',
             'exit 1'
-            ];
+        ];
     }
-    
+
     /**
-     * get string with current ID
+     * Get string with current ID
      * @return string
      */
-    public function getId(){
+    public function getId(): string
+    {
         return $this->_sPluginId;
     }
-    
+
     /**
-     * get string with plugin name (taken from plugin language file)
+     * Get string with plugin name (taken from plugin language file)
      * @return string
      */
-    public function getName(){
+    public function getName():string
+    {
         return $this->_t('plugin_name');
     }
-    
+
     /**
-     * get string with plugin description (taken from plugin language file)
+     * Get string with plugin description (taken from plugin language file)
      * @return string
      */
-    public function getDescription(){
+    public function getDescription(): string
+    {
         return $this->_t('description');
     }
+
     /**
-     * get array read from info.json
-     * @return type
+     * Get array read from info.json
+     * @return array
      */
-    public function getPluginInfos(){
+    public function getPluginInfos(): array
+    {
 
-        if ($this->_aPlugininfos){
+        if ($this->_aPlugininfos) {
             return $this->_aPlugininfos;
         }
-        
-        $oReflection=new ReflectionClass($this);
-        $sFile=dirname($oReflection->getFileName()) . '/info.json';
-        $this->_aPlugininfos= (file_exists($sFile))
+
+        $oReflection = new ReflectionClass($this);
+        $sFile = dirname($oReflection->getFileName()) . '/info.json';
+        $this->_aPlugininfos = (file_exists($sFile))
             ? json_decode(file_get_contents($sFile), 1)
-            : array('error'=> 'unable to read info file ['.$sFile.'].')
+            : ['error' => "unable to read info file [$sFile]."]
         ;
         return $this->_aPlugininfos;
     }
 
     /**
-     * get the file extension of created output file (from plugin info.json)
+     * Get the file extension of created output file (from plugin info.json)
+     * @return string
      */
-    public function getExtension(){
-        $aInfos=$this->getPluginInfos();
-        return isset($aInfos['extension']) ? '.'.$aInfos['extension'] : '';
+    public function getExtension(): string
+    {
+        $aInfos = $this->getPluginInfos();
+        return isset($aInfos['extension']) ? '.' . $aInfos['extension'] : '';
     }
+
     /**
-     * set outfile name including extension (from plugin metadata)
-     * @param  string  $sOutFilename  filename for output (without extension)
-     * @return array
+     * Get outfile name including extension (from plugin metadata)
+     * @return string
      */
-    public function getOutfile(){
-        return $this->_sOutfile.$this->getExtension();
+    public function getOutfile(): string
+    {
+        return $this->_sOutfile . $this->getExtension();
     }
+
     /**
-     * set outfile name
-     * @param  string  $sOutFilename  filename for output (without extension)
-     * @return array
+     * get current build dir
+     * @return string
      */
-    public function getBuildDir(){
+    public function getBuildDir(): string
+    {
         return $this->_sBuildDir;
     }
 
     // ----------------------------------------------------------------------
     // INTERFACE :: RENDERER
     // ----------------------------------------------------------------------
-    public function renderPluginBox(){
-        $sReturn='';        
-        $aInfos=$this->getPluginInfos();
 
-        return '<strong>'.$this->getName().'</strong> ('.$this->getId().')<br>
-                '.$this->getDescription();
+    /**
+     * Render plugin box as HTML to show in plugin overview
+     * @return string
+     */
+    public function renderPluginBox(): string
+    {
+        $aInfos = $this->getPluginInfos();
+
+        return '<strong>' . $this->getName() . '</strong> (' . $this->getId() . ')<br>
+                ' . $this->getDescription();
     }
 }
diff --git a/public_html/deployment/classes/classinfos.class.php b/public_html/deployment/classes/classinfos.class.php
index 26ca2b02c38792f8725cd64eef766a45fdea73c9..8f1f7e9ec540e9665bac456f844105ac74ee020d 100644
--- a/public_html/deployment/classes/classinfos.class.php
+++ b/public_html/deployment/classes/classinfos.class.php
@@ -18,7 +18,6 @@ class classinfos {
      */
     public function __construct($sClassname){
         if($sClassname) $this->setClassname($sClassname);
-        return true;
     }
     
     public function setClassname($sClassname){
@@ -73,7 +72,7 @@ class classinfos {
             $sReturn=preg_replace('/\@('.$sKey.')/U', '<span class="doctag">@'.$sKey.'</span>', $sReturn);
         }
         return $sReturn;
-        return $sReturn='<div class="comment">'. $sReturn . '</div>';
+        // return $sReturn='<div class="comment">'. $sReturn . '</div>';
         
     }
     
@@ -204,7 +203,7 @@ class classinfos {
         
         $source_code=file_get_contents($this->oRefClass->getFileName());
         
-        $sHtml.='<div class="sourcecode">';
+        $sHtml='<div class="sourcecode">';
         if ($sMode=="html"){
             $sHtml.='<h2 id="source">Source</h2>';
         }
@@ -343,7 +342,3 @@ class classinfos {
     
     
 }
-
-?>
-
-    
\ No newline at end of file
diff --git a/public_html/deployment/classes/config-replacement.class.php b/public_html/deployment/classes/config-replacement.class.php
index 93fb09a85b937b4526583168ff42687cdf247b2c..7b3f3d75eff17e1e77204c4f24f063111e806b7d 100644
--- a/public_html/deployment/classes/config-replacement.class.php
+++ b/public_html/deployment/classes/config-replacement.class.php
@@ -8,133 +8,153 @@ require_once 'project.class.php';
 /**
  * config-replacement class
  * reads templatefiles and scans its placeholders for replacements
- *
+ * 
+ * @deprecated It is used for Foreman replacements; TODO: remove this class
+ * 
  * @author hahn
+ * 
+ * 2024-08-23  v1.1  Axel Hahn  fix php parser problems
  */
-class configreplacement {
-    
+class configreplacement
+{
+
     /**
      * project class
-     * @var type 
+     * @var object
      */
     protected $_oProject = false;
-    protected $_sPhase = false;
-    protected $_aForemanReplacements = false;
 
-    
     /**
-     * init
+     * Phase of rollout
+     * @var string
+     */
+    protected $_sPhase = '';
+
+    /**
+     * Found replacements in Foreman
+     * @var array
+     */
+    protected array $_aForemanReplacements = [];
+
+
+    /**
+     * Constructor
      * @param string  $sProject  optional: project id; you can use setProject() too
-     * @return boolean
      */
-    public function __construct($sProject = false) {
-        if ($sProject){
+    public function __construct($sProject = '')
+    {
+        if ($sProject) {
             $this->setProject($sProject);
         }
-        return true;
     }
-    
+
 
     /**
-     * get an array with a flat list of all templatefiles of a build
+     * Get an array with a flat list of all templatefiles of a build
      * @return boolean|array
      */
-    public function getTemplatefiles(){
-        if (!$this->_sPhase){
+    public function getTemplatefiles(): bool|array
+    {
+        if (!$this->_sPhase) {
             return false;
         }
-        $aReturn = array();
+        $aReturn = [];
 
-        $aBuildfiles=$this->_oProject->getBuildfilesByPlace($this->_sPhase, 'onhold');
-        if (!$aBuildfiles){
-            $aBuildfiles=$this->_oProject->getBuildfilesByPlace($this->_sPhase, 'ready2install');
+        $aBuildfiles = $this->_oProject->getBuildfilesByPlace($this->_sPhase, 'onhold');
+        if (!$aBuildfiles) {
+            $aBuildfiles = $this->_oProject->getBuildfilesByPlace($this->_sPhase, 'ready2install');
         }
-        
-        if (!isset($aBuildfiles['types']['templates'])){
+
+        if (!isset($aBuildfiles['types']['templates'])) {
             return false;
         }
-        foreach ($aBuildfiles['types']['templates'] as $sFile){
-            $aReturn[]=$aBuildfiles['dir'].'/'.$sFile;
+        foreach ($aBuildfiles['types']['templates'] as $sFile) {
+            $aReturn[] = $aBuildfiles['dir'] . '/' . $sFile;
         }
         return $aReturn;
     }
 
     /**
      * get an array with all template files (basename) and its replacement fields
-     * @return array
+     * @return bool|array
      */
-    public function getReplacements(){
-        if (!$this->_sPhase){
+    public function getReplacements(): bool|array
+    {
+        if (!$this->_sPhase) {
             return false;
         }
-        $aFiles=$this->getTemplatefiles($this->_sPhase);
-        if (!$aFiles){
+        $aFiles = $this->getTemplatefiles();
+        if (!$aFiles) {
             return false;
         }
-        
-        $aReturn=array();
-        foreach ($aFiles as $sFile){
+
+        $aReturn = [];
+        foreach ($aFiles as $sFile) {
             // $sFile always exists because it was read from filesystem
-            $sContent=file_get_contents($sFile);
+            $sContent = file_get_contents($sFile);
 
             preg_match_all('/\@replace\[[\'\"](.*)[\'\"]\]/U', $sContent, $aMatches);
-            $aReturn[$sFile]=$aMatches[1];
-       }
+            $aReturn[$sFile] = $aMatches[1];
+        }
         return $aReturn;
     }
-    
+
     /**
-     * get effective hostgroup id of the current phase
+     * Get effective hostgroup id of the current phase
      * @return integer
      */
-    public function getForemanHostgroup(){
-        $aPrjConfig=$this->_oProject->getConfig();
+    public function getForemanHostgroup(): int
+    {
+        $aPrjConfig = $this->_oProject->getConfig();
         $iForemanHostgroupDefault = (int) $aPrjConfig['deploy']['foreman']['hostgroup'];
         $iForemanHostgroup = (int) $aPrjConfig['phases'][$this->_sPhase]['foreman-hostgroup'];
-        return ($iForemanHostgroup===OPTION_DEFAULT) ? $iForemanHostgroupDefault : $iForemanHostgroup;
+        return ($iForemanHostgroup === OPTION_DEFAULT) ? $iForemanHostgroupDefault : $iForemanHostgroup;
     }
-    
+
     /**
      * get replacement definitions from foreman
-     * @global type $aConfig
-     * @return boolean
+     * 
+     * @global array $aConfig
+     * 
+     * @return bool|array
      */
-    protected function _getForemanReplacement(){
+    protected function _getForemanReplacement(): bool|array
+    {
         global $aConfig;
-        
+
         $sProject = $this->_oProject->getId();
         // echo "DEBUG: project id = $sProject<br>";
-        
-        if (!$this->_sPhase){
+
+        if (!$this->_sPhase) {
             return false;
         }
-        
+
         // abort if no foreman connection was configured
         if (!isset($aConfig['foreman'])) {
             return false;
         }
 
         // return already cached result
-        if (isset($this->_aForemanReplacements[$sProject])){
+        if (isset($this->_aForemanReplacements[$sProject])) {
             return $this->_aForemanReplacements[$sProject];
         }
-        
+
         // rebuilt 
-        $this->_aForemanReplacements[$sProject]=false;
-        $iEffectiveHostgroup=$this->getForemanHostgroup();
+        $this->_aForemanReplacements[$sProject] = false;
+        $iEffectiveHostgroup = $this->getForemanHostgroup();
 
         // abort if no hostgroup was set
-        if($iEffectiveHostgroup<=0){
+        if ($iEffectiveHostgroup <= 0) {
             return false;
         }
 
         require_once 'foremanapi.class.php';
-        $oForeman=new ForemanApi($aConfig['foreman']);
-       
+        $oForeman = new ForemanApi($aConfig['foreman']);
+
         // get a host of this phase
-        $aHosts=$oForeman->read(array(
+        $aHosts = $oForeman->read(array(
             'request' => array(
-                array('hosts' ),
+                array('hosts'),
             ),
             'filter' => array(
                 'hostgroup_id' => $iEffectiveHostgroup,
@@ -142,102 +162,103 @@ class configreplacement {
             'response' => array('name', 'id'),
         ));
 
-        $sHost='';
-        $aHostsOfPhase=array();
-        
+        $sHost = '';
+        $aHostsOfPhase = array();
+
         // HACK: phases are part of the hostname .. but not "live" ... and special handling for demo
-        $aPrjConfig=$this->_oProject->getConfig();
-        $bIsDemo=(isset($aPrjConfig['fileprefix'])
-                && !strpos($aPrjConfig['fileprefix'], 'demo')===false);
-        $sPhase=$bIsDemo ? 'demo' : $this->_sPhase;
-        foreach($aHosts as $aName){
-            $sName=$aName['name'];
-            if (($sPhase==='live' &&
-                (
-                    strpos($sName, 'preview')===false
-                    && strpos($sName, 'stage')===false
-                    && strpos($sName, 'demo')===false
+        $aPrjConfig = $this->_oProject->getConfig();
+        $bIsDemo = (isset($aPrjConfig['fileprefix'])
+            && !strpos($aPrjConfig['fileprefix'], 'demo') === false);
+        $sPhase = $bIsDemo ? 'demo' : $this->_sPhase;
+        foreach ($aHosts as $aName) {
+            $sName = $aName['name'];
+            if (
+                ($sPhase === 'live' &&
+                    (
+                        strpos($sName, 'preview') === false
+                        && strpos($sName, 'stage') === false
+                        && strpos($sName, 'demo') === false
+                    )
                 )
-            )
-               || (strpos($sName, $sPhase))
-            ){
-                $sHost=$sHost ? $sHost : $sName;
-                $aHostsOfPhase[]=$sName;
+                || (strpos($sName, $sPhase))
+            ) {
+                $sHost = $sHost ? $sHost : $sName;
+                $aHostsOfPhase[] = $sName;
             }
         }
-        
+
         // get created yaml of this host
-        $sUrl=$aConfig['foreman']['api']."hosts/$sHost/externalNodes?name=$sHost";
-        $aData=$oForeman->makeRequest(array(
-            'url'=>$sUrl,
-            'method'=>'GET',
+        $sUrl = $aConfig['foreman']['api'] . "hosts/$sHost/externalNodes?name=$sHost";
+        $aData = $oForeman->makeRequest(array(
+            'url' => $sUrl,
+            'method' => 'GET',
         ));
-        
+
         // HACK: minify YAML of the host ... because it is too large for SPYC parser?
-        $sPart="---\n";
-        $bCopy=false;
-        foreach(explode("\n", $aData['body']) as $sLine){
-            if($bCopy){
-                if (strpos($sLine, '    ')===false){
-                    $bCopy=false;
+        $sPart = "---\n";
+        $bCopy = false;
+        foreach (explode("\n", $aData['body']) as $sLine) {
+            if ($bCopy) {
+                if (strpos($sLine, '    ') === false) {
+                    $bCopy = false;
                 } else {
                     // remove leading spaces and html entities...
-                    $sNewLine=  html_entity_decode(preg_replace('/^\ \ \ \ /', '', $sLine, 1));
-                    $sNewLine=str_replace('&#39;{', "'{", $sNewLine);
-                    $sNewLine=str_replace('}&#39;', "}'", $sNewLine);
-                    
+                    $sNewLine = html_entity_decode(preg_replace('/^\ \ \ \ /', '', $sLine, 1));
+                    $sNewLine = str_replace('&#39;{', "'{", $sNewLine);
+                    $sNewLine = str_replace('}&#39;', "}'", $sNewLine);
+
                     // fix json errors
-                    $sNewLine=str_replace(', }', " }", $sNewLine);
-                    $sNewLine=str_replace(',}', "}", $sNewLine);
-                    $sPart.=$sNewLine."\n";
+                    $sNewLine = str_replace(', }', " }", $sNewLine);
+                    $sNewLine = str_replace(',}', "}", $sNewLine);
+                    $sPart .= $sNewLine . "\n";
                 }
             }
-            if($sLine==='  iml-deployment-config:'){
-                $bCopy=true;
+            if ($sLine === '  iml-deployment-config:') {
+                $bCopy = true;
             }
             // echo 'DEBUG: '.($bCopy ? 'COPY':'SKIP').$sLine.'<br>';
         }
-        if (strstr($sPart, '|-')){
+        if (strstr($sPart, '|-')) {
             echo 'WARNING: the chars &quot;|-&quot; were found:<br><pre> '
-            .str_replace('|-', '<span class="replace">|-</span>', $sPart)
-            .'</pre><br>';
+                . str_replace('|-', '<span class="replace">|-</span>', $sPart)
+                . '</pre><br>';
         }
-        require_once __DIR__.'./../../vendor/spyc/spyc.php';
-        
+        require_once __DIR__ . './../../vendor/spyc/spyc.php';
+
         // echo 'DEBUG: <pre>'.print_r(spyc_load($aData['body']), 1).'</pre>';
-        $aYaml=spyc_load($sPart);
+        $aYaml = spyc_load($sPart);
         // echo 'DEBUG: <pre>'.print_r($aYaml, 1).'</pre>';
 
-       
-        if (!isset($aYaml[$sProject])){
+
+        if (!isset($aYaml[$sProject])) {
             return false;
         }
-        foreach ($aYaml as $sPrjId=>$aProject){
-            $aReturn=array();
-            foreach ($aProject as $sFile=>$aParams){
-                $aReturn[$sFile]=array();
-                if (isset($aParams['target'])){
-                    $aReturn[$sFile]['target']=$aParams['target'];
+        foreach ($aYaml as $sPrjId => $aProject) {
+            $aReturn = array();
+            foreach ($aProject as $sFile => $aParams) {
+                $aReturn[$sFile] = array();
+                if (isset($aParams['target'])) {
+                    $aReturn[$sFile]['target'] = $aParams['target'];
                 }
-                if (isset($aParams['replace'])){
-                    $aReplace=json_decode($aParams['replace'], 1);
-                    $aReturn[$sFile]['replace_source']=$aReplace;
-                    foreach ($aReplace as $sVarname=>$value){
-                        if (isset($value['_'.$this->_sPhase.'_'])){
-                            $value=$value['_'.$this->_sPhase.'_'];
+                if (isset($aParams['replace'])) {
+                    $aReplace = json_decode($aParams['replace'], 1);
+                    $aReturn[$sFile]['replace_source'] = $aReplace;
+                    foreach ($aReplace as $sVarname => $value) {
+                        if (isset($value['_' . $this->_sPhase . '_'])) {
+                            $value = $value['_' . $this->_sPhase . '_'];
                         }
-                        $aReturn[$sFile]['replace'][$sVarname]=$value;
+                        $aReturn[$sFile]['replace'][$sVarname] = $value;
                     }
                 }
             }
-            $this->_aForemanReplacements[$sPrjId]=array(
-                'phase'=>$this->_sPhase,
-                'rules'=>$aReturn,
-                'host'=>$sHost,
-                'yaml'=>$sPart,
-                'hostgroup'=>$iEffectiveHostgroup,
-                'hostsall'=>$aHosts,
-                'hostsphase'=>$aHostsOfPhase,
+            $this->_aForemanReplacements[$sPrjId] = array(
+                'phase' => $this->_sPhase,
+                'rules' => $aReturn,
+                'host' => $sHost,
+                'yaml' => $sPart,
+                'hostgroup' => $iEffectiveHostgroup,
+                'hostsall' => $aHosts,
+                'hostsphase' => $aHostsOfPhase,
             );
         }
 
@@ -245,107 +266,117 @@ class configreplacement {
     }
 
     /**
-     * get foreman base url ... if foreman was activated in the setup
+     * Get foreman base url ... if foreman was activated in the setup
      * 
      * @global array  $aConfig  ci config
      * @return string
      */
-    private function _getForemanBaseUrl(){
+    private function _getForemanBaseUrl(): string
+    {
         global $aConfig;
         return (isset($aConfig['foreman']['api']))
             ? $aConfig['foreman']['api']
             : false
         ;
     }
-    
+
     /**
-     * get html code for links to edit each host of the current phase in foreman
+     * Get html code for links to edit each host of the current phase in foreman
+     * It returns false if foreman is not activated or there is no phase.
      * 
-     * @return boolean
+     * @return bool|string
      */
-    public function getForemanlink2Host(){
-        $sForemanurl=$this->_getForemanBaseUrl();
-        if (!$sForemanurl){
+    public function getForemanlink2Host(): bool|string
+    {
+        $sForemanurl = $this->_getForemanBaseUrl();
+        if (!$sForemanurl) {
             return false;
         }
-        $aTmp=$this->_getForemanReplacement();
-        if (!isset($aTmp['hostsphase'])){
+        $aTmp = $this->_getForemanReplacement();
+        if (!isset($aTmp['hostsphase'])) {
             return false;
         }
         require_once 'htmlguielements.class.php';
-        $oHtml=new htmlguielements();
-        $sReturn='';
-        foreach ($aTmp['hostsphase'] as $sHost){
-            $sReturn.=$oHtml->getLinkButton(array(
-                'href'=>$sForemanurl.'hosts/'.$aTmp['host'],
-                'target'=>'_foreman',
-                'title'=>t('edit'),
-                'icon'=>'host',
-                'label'=>t('host').' '.$aTmp['host'],
-            )).' ';
+        $oHtml = new htmlguielements();
+        $sReturn = '';
+        foreach ($aTmp['hostsphase'] as $sHost) {
+            $sReturn .= $oHtml->getLinkButton(array(
+                'href' => $sForemanurl . 'hosts/' . $aTmp['host'],
+                'target' => '_foreman',
+                'title' => t('edit'),
+                'icon' => 'host',
+                'label' => t('host') . ' ' . $aTmp['host'],
+            )) . ' ';
         }
         return $sReturn;
     }
 
     /**
-     * get html code for a link to edit hostgroup in foreman
+     * Get html code for a link to edit hostgroup in foreman
+     * It returns false if foreman is not activated or there is no hostgroup in the project phase
      * 
-     * @return boolean
+     * @return bool|string
      */
-    public function getForemanlink2Hostgroup(){
-        $iEffectiveHostgroup=$this->getForemanHostgroup();
-        if($iEffectiveHostgroup<=0){
+    public function getForemanlink2Hostgroup(): bool|string
+    {
+        $iEffectiveHostgroup = $this->getForemanHostgroup();
+        if ($iEffectiveHostgroup <= 0) {
             return false;
         }
-        $sForemanurl=$this->_getForemanBaseUrl();
-        if (!$sForemanurl){
+        $sForemanurl = $this->_getForemanBaseUrl();
+        if (!$sForemanurl) {
             return false;
         }
         require_once 'htmlguielements.class.php';
-        $oHtml=new htmlguielements();
+        $oHtml = new htmlguielements();
         return $oHtml->getLinkButton(array(
-            'href'=>$sForemanurl.'hostgroups/'.$iEffectiveHostgroup.'/edit',
-            'target'=>'_foreman',
-            'title'=>t('edit'),
-            'icon'=>'hostgroup',
-            'label'=>sprintf(t('foreman-hostgroup-id'), $iEffectiveHostgroup),
+            'href' => $sForemanurl . 'hostgroups/' . $iEffectiveHostgroup . '/edit',
+            'target' => '_foreman',
+            'title' => t('edit'),
+            'icon' => 'hostgroup',
+            'label' => sprintf(t('foreman-hostgroup-id'), $iEffectiveHostgroup),
         ));
-        
+
     }
-    
+
     /**
      * get replacements in foreman 
-     * @return type
+     * @return bool|array
      */
-    public function getForemanReplacements(){
+    public function getForemanReplacements(): bool|array
+    {
         return $this->_getForemanReplacement();
     }
-    
+
     /**
      * switch to a project
-     * @param type $sProject
+     * @param string $sProject  project id
+     * @param string $sPhase    optional: a phase; one of preview|stage|live
+     * @return boolean
      */
-    public function setProject($sProject, $sPhase=false){
+    public function setProject(string $sProject, string $sPhase = ''): bool
+    {
         $this->_oProject = new project($sProject);
-        $this->_aForemanReplacements=false;
+        $this->_aForemanReplacements = [];
         $this->setPhase($sPhase);
         return true;
     }
-    
+
     /**
-     * set a phase of a project
+     * Set a phase of a project
      * @param string  $sPhase  name of valid phase
      * @return boolean
      */
-    public function setPhase($sPhase=false){
-        $this->_sPhase=false;
-        if (!$sPhase){
-            $sPhase=$this->_oProject->getNextPhase(false);
+    public function setPhase(string $sPhase = ''): bool
+    {
+        $this->_sPhase = false;
+        if (!$sPhase) {
+            $sPhase = $this->_oProject->getNextPhase(false);
         }
         if (!$sPhase || !$this->_oProject->isActivePhase($sPhase)) {
             return false;
         }
-        $this->_sPhase=$sPhase;
+        $this->_sPhase = $sPhase;
         return true;
     }
 }
diff --git a/public_html/deployment/classes/foremanapi.class.php b/public_html/deployment/classes/foremanapi.class.php
index 1e7030289d78f96d96c5ae22b67e88429420f82e..4365b1a12907b2b53cec1d874663273a02a7db37 100644
--- a/public_html/deployment/classes/foremanapi.class.php
+++ b/public_html/deployment/classes/foremanapi.class.php
@@ -4,6 +4,8 @@
  * 
  * foreman access to API
  * 
+ * @deprecated Foreman was too much hartcoded. TODO: remove this class
+ * 
  * @example
  * in project class
  * $oForeman=new ForemanApi($this->_aConfig['foreman']);
@@ -116,13 +118,13 @@ class ForemanApi {
     
     /**
      * last request
-     * @var type 
+     * @var array
      */
     protected $_aRequest=array();
     
     /**
      * last response
-     * @var type 
+     * @var array
      */
     protected $_aResponse=array();
     
@@ -135,12 +137,9 @@ class ForemanApi {
     public function __construct($aCfg) {
         if(!isset($aCfg['api'])){
             die("ERROR: class ".__CLASS__." must be initialized with an array containing api config for foreman.");
-            return false;
         }
         $this->_aCfg=$aCfg;
-        
-        return true;
-    }
+            }
     
     // ----------------------------------------------------------------------
     // private functions
@@ -159,10 +158,10 @@ class ForemanApi {
     
     /**
      * search url prefix in $this->_aAllowedUrls by given key
-     * @param type $sFunction
-     * @return type
+     * @param string $sFunction
+     * @return string
      */
-    protected function _guessPrefixUrl($sFunction=false){
+    protected function _guessPrefixUrl($sFunction=''){
         $sReturn='';
         /*
         if (!$sFunction){
@@ -232,7 +231,7 @@ class ForemanApi {
      * - postdata; for POST only
      * 
      * @param array   $aRequest   arrayurl for Foreman API
-     * @return string
+     * @return array
      */
     protected function _httpCall($aRequest=false, $iTimeout = 15) {
         if ($aRequest){
@@ -433,7 +432,7 @@ class ForemanApi {
 
     /**
      * check for missing config entries
-     * @return type
+     * @return bool
      */
     public function selfcheck() {
         $sOut='';
@@ -498,7 +497,7 @@ class ForemanApi {
      *      - list of keys, i.e. array('id', 'title')
      * 
      * @param array  $aOptions  
-     * @return array
+     * @return bool|array
      */
     public function read($aOptions){
         $this->_aRequest=$aOptions;
@@ -512,7 +511,7 @@ class ForemanApi {
     
     /**
      * TODO
-     * @param type $aOptions
+     * @param array $aOptions
      */
     public function update($aOptions){
         /*
@@ -524,7 +523,7 @@ class ForemanApi {
     
     /**
      * TODO
-     * @param type $aOptions
+     * @param array $aOptions
      */
     public function delete($aOptions){
         /*
@@ -573,7 +572,7 @@ class ForemanApi {
             [local_port] => 33906
         )
      * @param string $sKey  get value of given key only
-     * @return any
+     * @return mixed
      */
     public function getResponseInfo($sKey=false){
         
diff --git a/public_html/deployment/classes/formgen.class.php b/public_html/deployment/classes/formgen.class.php
index f60936bbc3b09a84991cf4f6a5375ec6cad417bb..bd226b00d435c3452efc153997bd28adfa123324 100644
--- a/public_html/deployment/classes/formgen.class.php
+++ b/public_html/deployment/classes/formgen.class.php
@@ -9,130 +9,150 @@
   feature complete.
 
   ---------------------------------------------------------------------
-  2013-11-08  Axel <axel.hahn@iml.unibe.ch>
+  Axel <axel.hahn@iml.unibe.ch>
+  2013-11-08        Axel
+  2024-08-23  v1.1  Axel  php8 only; added variable types; short array syntax
   ###################################################################### */
 
-class formgen {
+class formgen
+{
 
-    var $aForm = array();
+    var $aForm = [];
     var $sRequired = ' <span title="Eingabe ist erforderlich"><span style="color:#c00;">*</span></span>';
 
     /**
-     * constructor
+     * Constructor
      * @param array $aNewFormData
-     * @return boolean
      */
-    public function __construct($aNewFormData = array()) {
-        if (is_array($aNewFormData) && count($aNewFormData)){
-            return $this->setFormarray($aNewFormData);
+    public function __construct($aNewFormData = [])
+    {
+        if (is_array($aNewFormData) && count($aNewFormData)) {
+            $this->setFormarray($aNewFormData);
         }
-        return true;
     }
 
     /**
-     * set a new array
+     * Set a new array for a new form
      * @param array $aNewFormData
      * @return boolean
      */
-    public function setFormarray($aNewFormData = array()) {
+    public function setFormarray($aNewFormData = [])
+    {
         if (!is_array($aNewFormData) || !count($aNewFormData)) {
             return false;
         }
-        return $this->aForm = $aNewFormData;
+        $this->aForm = $aNewFormData;
+        return true;
     }
 
     /**
-     * get html code for a completely rendered form
-     * @param string $sFormId
+     * Get html code for a completely rendered form
+     * @param  string $sFormId
      * @return string html output
      */
-    public function renderHtml($sFormId) {
+    public function renderHtml(string $sFormId): string
+    {
         $sReturn = false;
         if (!isset($this->aForm[$sFormId])) {
-            die("ERROR: " . __CLASS__ . ":" . __FUNCTION__ . " - form id " . $sFormId . " does not exist.");
+            throw new Exception("ERROR: " . __CLASS__ . ":" . __FUNCTION__ . " - form id " . $sFormId . " does not exist.");
         }
         // FORM tag
-        $sReturn.='<form ';
+        $sReturn .= '<form ';
         if (isset($this->aForm[$sFormId]["meta"])) {
-            foreach (array("method", "action", "target", "accept-charset", "class", "id", "name") as $sAttr) {
+            foreach (["method", "action", "target", "accept-charset", "class", "id", "name"] as $sAttr) {
                 if (isset($this->aForm[$sFormId]["meta"][$sAttr])) {
-                    $sReturn.=$sAttr . '="' . $this->aForm[$sFormId]["meta"][$sAttr] . '" ';
+                    $sReturn .= $sAttr . '="' . $this->aForm[$sFormId]["meta"][$sAttr] . '" ';
                 }
             }
         }
-        $sReturn.='>';
-        
+        $sReturn .= '>';
+
         // ... and all its elements
         foreach ($this->aForm[$sFormId]["form"] as $elementKey => $elementData) {
-            $sReturn.=$this->renderHtmlElement($elementKey, $elementData);
+            $sReturn .= $this->renderHtmlElement($elementKey, $elementData);
         }
-        $sReturn.='</form>';
+        $sReturn .= '</form>';
 
         return $sReturn;
     }
 
     /**
-     * add html attributes if they exist
+     * Add html attributes if they exist
      * @param array $aAttributes  list of attributes to search for
      * @param array $elementData  array of form element
      * @return string
      */
-    private function _addHtmlAtrributes($aAttributes, $elementData) {
+    private function _addHtmlAtrributes(array $aAttributes, array $elementData): string
+    {
         $sReturn = false;
         foreach ($aAttributes as $sAtrr) {
             if (isset($elementData[$sAtrr]) && $elementData[$sAtrr]) {
-                $sReturn.=($sReturn ? ' ' : '')
-                    .$sAtrr . '="' . $elementData[$sAtrr] . '"'
+                $sReturn .= ($sReturn ? ' ' : '')
+                    . $sAtrr . '="' . $elementData[$sAtrr] . '"'
                 ;
             }
         }
         return $sReturn;
     }
 
-    private function _addLabel($sLabel, $sFor, $sClass = false) {
-        $sReturn = false;
-        $sReturn = '<label for="' . $sFor . '"';
-        if ($sClass)
-            $sReturn.=' class="' . $sClass . '"';
-        $sReturn.='>' . $sLabel . '</label>';
-        $sReturn.="\n";
-        return $sReturn;
+    /**
+     * Add a label next to a form element
+     * @param string $sLabel  Labeltext to show
+     * @param string $sFor    for attribute to ad (points to the id of the form element)
+     * @param string $sClass  css class
+     * @return string
+     */
+    private function _addLabel(string $sLabel, string $sFor, string $sClass = ''): string
+    {
+        return "<label for=\"$sFor\""
+            . ($sClass ? " class=\"$sClass\"" : '')
+            . ">$sLabel</label>"
+            . "\n"
+            ;
     }
 
-    private function _checkReqiredKeys($aArray, $aRequiredKeys, $sLabel = false) {
+    /**
+     * Ensure that all required keys are set
+     * @param  array  $aArray         given array
+     * @param  array  $aRequiredKeys  set of required keys
+     * @param  string $sLabel         form label - will be shown in error message
+     * @throws \Exception
+     * @return bool
+     */
+    private function _checkReqiredKeys(array $aArray, array $aRequiredKeys, string $sLabel = ''): bool
+    {
         $bReturn = true;
         foreach ($aRequiredKeys as $sKey) {
             if (!isset($aArray[$sKey])) {
-                die("ERROR: $sLabel<br>Missing key \"$sKey\" in the array of a form element:<pre>" . print_r($aArray, true) . "</pre>");
-                $bReturn = false;
+                throw new Exception("ERROR: $sLabel<br>Missing key \"$sKey\" in the array of a form element:<pre>" . print_r($aArray, true) . "</pre>");
             }
         }
         return $bReturn;
     }
 
     /**
-     * render a single form element
+     * Render a single form element
      * @param string $sId          id of a form element
      * @param array  $elementData  array of form element
      * @return string html output
      */
-    public function renderHtmlElement($sId, $elementData) {
+    public function renderHtmlElement(string $sId, array $elementData): string
+    {
         $sReturn = false;
-        $aAllowedHtmlAttributes = array();
         $sDefaultAttributes = ""
-                . "class,"
-                
-                // events ... see https://developer.mozilla.org/en-US/docs/Web/API/Element
-                . "onauxclick,onclick,ondblclick,oncontextmenu,onfocusin,onfocusout,"
-                . "onkeydown,onkeypress,onkeyup,"
-                . "onmousedown,onmouseenter,onmouseleave,onmousemove,onmouseout,onmouseover,onmouseup,"
-                //
-                . "title"
-                ;
+            . "class,"
+
+            // events ... see https://developer.mozilla.org/en-US/docs/Web/API/Element
+            . "onauxclick,onclick,ondblclick,oncontextmenu,onfocusin,onfocusout,"
+            . "onkeydown,onkeypress,onkeyup,"
+            . "onmousedown,onmouseenter,onmouseleave,onmousemove,onmouseout,onmouseover,onmouseup,"
+            //
+            . "title"
+        ;
 
         if (!isset($elementData["type"])) {
             print_r($elementData);
-            die("ERROR: " . __CLASS__ . ":" . __FUNCTION__ . " - key &quot;type&quot; does not exist.");
+            throw new Exception("ERROR: " . __CLASS__ . ":" . __FUNCTION__ . " - key &quot;type&quot; does not exist.");
         }
 
         $sFormElement = false;
@@ -144,36 +164,36 @@ class formgen {
 
         if (isset($elementData["label"])) {
             $sLabelText = $elementData["label"];
-            $sLabelText.=(isset($elementData["required"]) && $elementData["required"]) ? $this->sRequired : '';
+            $sLabelText .= (isset($elementData["required"]) && $elementData["required"]) ? $this->sRequired : '';
         }
 
         switch ($elementData["type"]) {
             case "button":
-                $this->_checkReqiredKeys($elementData, array("value"));
-                $elementData["class"]=$elementData["class"] ? $elementData["class"] : "btn btn-default";
-                $sFormElement.='    <button id="' . $sId . '" ';
-                $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,checked,name"), $elementData);
-                $sFormElement.='>' . $elementData["value"] . '</button>';
-                $sFormElement.="\n";
+                $this->_checkReqiredKeys($elementData, ["value"]);
+                $elementData["class"] = $elementData["class"] ? $elementData["class"] : "btn btn-default";
+                $sFormElement .= '    <button id="' . $sId . '" ';
+                $sFormElement .= $this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,checked,name"), $elementData);
+                $sFormElement .= '>' . $elementData["value"] . '</button>';
+                $sFormElement .= "\n";
 
                 $sHtmlDefault = $sFormElement;
                 break;
 
             case "checkbox":
-                $this->_checkReqiredKeys($elementData, array("name"));
+                $this->_checkReqiredKeys($elementData, ["name"]);
                 foreach ($elementData["options"] as $idOption => $aOptionData) {
-                    $sFormElement.="\n".'<div class="checkbox">';
+                    $sFormElement .= "\n" . '<div class="checkbox">';
                     $s = preg_replace('/\W/iu', '', $sId . $idOption);
                     $sOptionId = preg_replace('/[äöüß]/i', '', $s);
-                    $sFormElement.='    <input type="checkbox" id="' . $sOptionId . '" value="' . (isset($aOptionData["value"]) ? $aOptionData["value"] : $idOption) . '" ';
-                    $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,checked"), $aOptionData);
-                    $sFormElement.=' name="' . $elementData["name"] . '[]"';
-                    $sFormElement.='/><label for="' . $sOptionId . '">' . $aOptionData["label"] . '</label></div>';
+                    $sFormElement .= '    <input type="checkbox" id="' . $sOptionId . '" value="' . (isset($aOptionData["value"]) ? $aOptionData["value"] : $idOption) . '" ';
+                    $sFormElement .= $this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,checked"), $aOptionData);
+                    $sFormElement .= ' name="' . $elementData["name"] . '[]"';
+                    $sFormElement .= '/><label for="' . $sOptionId . '">' . $aOptionData["label"] . '</label></div>';
                 }
-                $sFormElement.="\n";
+                $sFormElement .= "\n";
                 // $sLabelElement.='<span class="help-block">' . $sLabelText . '</span>';
-                $sLabelElement.='<div class="col-sm-2">' . $sLabelText . '</div>';
-                $sLabelElement.="\n";
+                $sLabelElement .= '<div class="col-sm-2">' . $sLabelText . '</div>';
+                $sLabelElement .= "\n";
 
                 // $sHtmlDefault = $sLabelElement . $sFormElement;
                 $sHtmlDefault = $sLabelElement . '<div class="col-sm-10">' . "\n" . $sFormElement . '</div>' . "\n";
@@ -181,11 +201,11 @@ class formgen {
                 break;
 
             case "hidden":
-                $this->_checkReqiredKeys($elementData, array("value"));
-                $sFormElement.='    <input type="hidden" id="' . $sId . '" ';
-                $sFormElement.=$this->_addHtmlAtrributes(explode(",", "name,value"), $elementData);
-                $sFormElement.=" />";
-                $sFormElement.="\n";
+                $this->_checkReqiredKeys($elementData, ["value"]);
+                $sFormElement .= '    <input type="hidden" id="' . $sId . '" ';
+                $sFormElement .= $this->_addHtmlAtrributes(explode(",", "name,value"), $elementData);
+                $sFormElement .= " />";
+                $sFormElement .= "\n";
 
                 $sHtmlDefault = $sFormElement . "\n";
                 break;
@@ -196,17 +216,17 @@ class formgen {
                 break;
 
             case "radio":
-                $this->_checkReqiredKeys($elementData, array("name"));
+                $this->_checkReqiredKeys($elementData, ["name"]);
                 foreach ($elementData["options"] as $idOption => $aOptionData) {
-                    $sFormElement.="\n".'<div class="radio">';
+                    $sFormElement .= "\n" . '<div class="radio">';
                     $s = preg_replace('/\W/iu', '', $sId . $idOption);
                     $sOptionId = preg_replace('/[äöüß]/i', '', $s);
-                    $sFormElement.='    <input type="radio" id="' . $sOptionId . '" value="' . (isset($aOptionData["value"]) ? $aOptionData["value"] : $idOption) . '" ';
-                    $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,checked,disabled"), $aOptionData);
-                    $sFormElement.=" " . $this->_addHtmlAtrributes(explode(",", "name"), $elementData);
-                    $sFormElement.='/><label for="' . $sOptionId . '">' . $aOptionData["label"] . '</label></div>';
+                    $sFormElement .= '    <input type="radio" id="' . $sOptionId . '" value="' . (isset($aOptionData["value"]) ? $aOptionData["value"] : $idOption) . '" ';
+                    $sFormElement .= $this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,checked,disabled"), $aOptionData);
+                    $sFormElement .= " " . $this->_addHtmlAtrributes(explode(",", "name"), $elementData);
+                    $sFormElement .= '/><label for="' . $sOptionId . '">' . $aOptionData["label"] . '</label></div>';
                 }
-                $sFormElement.="\n";
+                $sFormElement .= "\n";
 
                 if ($sLabelText) {
                     // $sLabelElement.='<span class="help-block">' . $sLabelText . '</span>' . "\n";
@@ -224,24 +244,24 @@ class formgen {
 
             case "select":
                 // HINWEIS optgroups werden nicht unterstuezt - nur einfache Listen
-                $this->_checkReqiredKeys($elementData, array("name"));
-                $sDivClass=(isset($elementData["inline"]) && $elementData["inline"])?"form-group":"col-sm-10";
-                $elementData['class'].=" form-control";
-                $sFormElement.='<div class="'.$sDivClass.'">'."\n".'<select id="' . $sId . '" ';
-                $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,name,onchange"), $elementData);
-                $sFormElement.=">\n";
+                $this->_checkReqiredKeys($elementData, ["name"]);
+                $sDivClass = (isset($elementData["inline"]) && $elementData["inline"]) ? "form-group" : "col-sm-10";
+                $elementData['class'] .= " form-control";
+                $sFormElement .= '<div class="' . $sDivClass . '">' . "\n" . '<select id="' . $sId . '" ';
+                $sFormElement .= $this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,name,onchange"), $elementData);
+                $sFormElement .= ">\n";
                 foreach ($elementData["options"] as $idOption => $aOptionData) {
                     $s = preg_replace('/\W/iu', '', $sId . $idOption);
                     $sOptionId = preg_replace('/[äöüß]/i', '', $s);
-                    $sFormElement.='    <option value="' . (isset($aOptionData["value"]) ? $aOptionData["value"] : $idOption) . '" ';
-                    $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,selected"), $aOptionData);
-                    $sFormElement.='>' . $aOptionData["label"] . '</option>' . "\n";
+                    $sFormElement .= '    <option value="' . (isset($aOptionData["value"]) ? $aOptionData["value"] : $idOption) . '" ';
+                    $sFormElement .= $this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,selected"), $aOptionData);
+                    $sFormElement .= '>' . $aOptionData["label"] . '</option>' . "\n";
                 }
-                $sFormElement.="</select></div>\n";
+                $sFormElement .= "</select></div>\n";
 
                 if ($sLabelText) {
                     // $sLabelElement.='<span class="help-block">' . $sLabelText . '</span>' . "\n";
-                    $sLabelClass=(isset($elementData["inline"]) && $elementData["inline"])?"":"col-sm-2";
+                    $sLabelClass = (isset($elementData["inline"]) && $elementData["inline"]) ? "" : "col-sm-2";
                     $sLabelElement = $this->_addLabel($sLabelText, $sId, $sLabelClass);
                 }
 
@@ -252,40 +272,40 @@ class formgen {
                 break;
 
             case "submit":
-                $this->_checkReqiredKeys($elementData, array("value"));
-                $sClass="btn btn-primary ";
-                if (isset($elementData["class"])){
-                    $sClass.=$elementData["class"];
+                $this->_checkReqiredKeys($elementData, ["value"]);
+                $sClass = "btn btn-primary ";
+                if (isset($elementData["class"])) {
+                    $sClass .= $elementData["class"];
                 }
-                $elementData["class"]=$sClass;
-                $sFormElement.='    <button id="' . $sId . '" type="submit" ';
-                $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes"), $elementData);
-                $sFormElement.='>' . $elementData["value"] . '</button>';
-                $sFormElement.="\n";
+                $elementData["class"] = $sClass;
+                $sFormElement .= '    <button id="' . $sId . '" type="submit" ';
+                $sFormElement .= $this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes"), $elementData);
+                $sFormElement .= '>' . $elementData["value"] . '</button>';
+                $sFormElement .= "\n";
 
                 $sHtmlDefault = $sFormElement;
                 break;
 
             case "text":
             case "password":
-                $this->_checkReqiredKeys($elementData, array("name"));
-                $sFormElement.='    <input type="'.$elementData["type"].'" id="' . $sId . '" class="form-control col-sm-10" ';
+                $this->_checkReqiredKeys($elementData, ["name"]);
+                $sFormElement .= '    <input type="' . $elementData["type"] . '" id="' . $sId . '" class="form-control col-sm-10" ';
                 $aAllowedHtmlAttributes["text"] = explode(",", "");
-                $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,name,autocomplete,autofocus,list,disabled,onchange,pattern,placeholder,required,size,value"), $elementData);
-                // $sFormElement.=$this->_addHtmlAtrributes(array("name", "value", "size", "placeholder", "required"), $elementData);
+                $sFormElement .= $this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,name,autocomplete,autofocus,list,disabled,onchange,pattern,placeholder,required,size,value"), $elementData);
+                // $sFormElement.=$this->_addHtmlAtrributes(["name", "value", "size", "placeholder", "required"], $elementData);
                 // IE: Return abfangen lassen
                 // $sFormElement.=' onkeypress="return checkKey(event);"';
-                $sFormElement.=' />';
-                $sFormElement.="\n";
+                $sFormElement .= ' />';
+                $sFormElement .= "\n";
 
-                if (isset($elementData["class"]) && $elementData["inline"]){
+                if (isset($elementData["class"]) && $elementData["inline"]) {
                     $sLabelElement = $this->_addLabel($sLabelText, $sId, "col-sm-2");
                     // $sHtmlDefault = $sLabelElement . "\n" . $sFormElement . "\n";
-                    $sHtmlDefault = $sLabelElement . '<div class="col-sm-3">' . "\n" . $sFormElement . '</div>' . "\n";                    
+                    $sHtmlDefault = $sLabelElement . '<div class="col-sm-3">' . "\n" . $sFormElement . '</div>' . "\n";
                 } else {
                     $sLabelElement = $this->_addLabel($sLabelText, $sId, "col-sm-2");
                     // $sHtmlDefault = $sLabelElement . '<div class="controls">' . "\n" . $sFormElement . '</div>' . "\n";                    
-                    $sHtmlDefault = $sLabelElement . '<div class="col-sm-10">' . "\n" . $sFormElement . '</div>' . "\n";                    
+                    $sHtmlDefault = $sLabelElement . '<div class="col-sm-10">' . "\n" . $sFormElement . '</div>' . "\n";
                 }
 
                 $sHtmlTable = '<td>' . $sLabelText . '</td><td>' . $sFormElement . '</td>';
@@ -293,13 +313,13 @@ class formgen {
                 break;
 
             case "textarea":
-                $this->_checkReqiredKeys($elementData, array("name"));
-                $sFormElement.='    <textarea id="' . $sId . '" class="form-control col-sm-10" ';
+                $this->_checkReqiredKeys($elementData, ["name"]);
+                $sFormElement .= '    <textarea id="' . $sId . '" class="form-control col-sm-10" ';
                 $aAllowedHtmlAttributes["text"] = explode(",", "");
-                $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,name,onchange,placeholder,required,cols,rows"), $elementData);
-                // $sFormElement.=$this->_addHtmlAtrributes(array("name", "value", "size", "placeholder", "required"), $elementData);
-                $sFormElement.='>'.$elementData['value'].'</textarea>';
-                $sFormElement.="\n";
+                $sFormElement .= $this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,name,onchange,placeholder,required,cols,rows"), $elementData);
+                // $sFormElement.=$this->_addHtmlAtrributes(["name", "value", "size", "placeholder", "required"], $elementData);
+                $sFormElement .= '>' . $elementData['value'] . '</textarea>';
+                $sFormElement .= "\n";
 
                 $sLabelElement = $this->_addLabel($sLabelText, $sId, "control-label col-sm-2");
 
@@ -311,31 +331,29 @@ class formgen {
 
             default:
                 die("ERROR: " . __CLASS__ . ":" . __FUNCTION__ . " - formelement type " . $elementData["type"] . " ist not supported (yet).");
-                break;
         }
 
         // Default or table mode?
         if (isset($elementData["mode"]) && $elementData["mode"] == 'table' && $sHtmlTable) {
             $sHtmlDefault = $sHtmlTable;
         } else {
-            if ($elementData["type"] != "button" 
-                && $elementData["type"] != "fieldset" 
+            if (
+                $elementData["type"] != "button"
+                && $elementData["type"] != "fieldset"
                 && $elementData["type"] != "markup"
                 && $elementData["type"] != "hidden"
             ) {
-                if (!isset($elementData["inline"]) || !$elementData["inline"]){
+                if (!isset($elementData["inline"]) || !$elementData["inline"]) {
                     // $sHtmlDefault = "<fieldset>" . $sHtmlDefault . "</fieldset>\n";
-                    $sHtmlDefault = '<div class="form-group row">' . $sHtmlDefault . '</div>'."\n";
+                    $sHtmlDefault = '<div class="form-group row">' . $sHtmlDefault . '</div>' . "\n";
                 }
             }
         }
 
-        $sReturn.="<!-- " . $elementData["type"] . " -->\n";
-        $sReturn.=$sHtmlDefault . "\n";
+        $sReturn .= "<!-- " . $elementData["type"] . " -->\n";
+        $sReturn .= $sHtmlDefault . "\n";
 
         return $sReturn;
     }
 
 }
-
-?>
\ No newline at end of file
diff --git a/public_html/deployment/classes/htmlelements.class.php b/public_html/deployment/classes/htmlelements.class.php
index 0296b1783d125690e684151780a94bbe082b9499..b890fda4dd77302de87fa59e603fb353ce763543 100755
--- a/public_html/deployment/classes/htmlelements.class.php
+++ b/public_html/deployment/classes/htmlelements.class.php
@@ -15,27 +15,33 @@
  *    - icon  - will be added as <i class="[icon value]"></i> to the label
  * 
  * @author Axel
+ * 
+ * 2024-07-04  <axel.hahn@unibe.ch>  added type declarations; update php docs
+ * 2024-08-26  <axel.hahn@unibe.ch>  remove unneeded methods; simplify icon methods; update phpdocs
  */
-class htmlelements {
+class htmlelements
+{
 
     /**
-     * set of auto generated icon prefixes
-     * @var type 
+     * Extracted label from array with attributes
+     * @var string
      */
-    var $_aIcons=array(
-            // 'fa-'=>'fa ',
-        );
-    
     var $_sLabel = '';
-    var $_aAttributes = array();
-    
+
+    /**
+     * Array of attributes for a html tag
+     * @var array
+     */
+    var $_aAttributes = [];
+
 
     // ----------------------------------------------------------------------
     // CONSTRUCTOR
     // ----------------------------------------------------------------------
-    
-    public function __construct() {
-        return true;
+
+    public function __construct()
+    {
+        // nothiung here
     }
 
     // ----------------------------------------------------------------------
@@ -43,27 +49,28 @@ class htmlelements {
     // PRIVATE FUNCTIONS 
     // 
     // ----------------------------------------------------------------------
-    
-    
+
+
     /**
      * generate html attibutes with all internal attributes key -> values
+     * to be added in opening tag
      * @return string
      */
-    protected function _addAttributes() {
+    protected function _addAttributes(): string
+    {
         $sReturn = '';
         foreach ($this->_aAttributes as $sAttr => $sValue) {
-            if(is_array($sValue)){
-                echo "ERROR: an html tag was defined with array in attribute [$sAttr]:<br><pre>".print_r($this->_aAttributes, 1)."</pre>";
+            if (is_array($sValue)) {
+                echo "ERROR: an html tag was defined with array in attribute [$sAttr]:<br><pre>" . print_r($this->_aAttributes, 1) . "</pre>";
             }
-            $sReturn .= ' '.$sAttr . '="' . $sValue . '"';
-            
+            $sReturn .= " $sAttr=\"$sValue\"";
         }
         return $sReturn;
     }
-    
-    
+
+
     /**
-     * internal helper: fetch all attributes from key-value hash; 
+     * Internal helper: fetch all attributes from key-value hash; 
      * Specialties here:
      * - label will be extracted from key 'label' 
      * - and optional existing key 'icon' will be added at beginning of a label
@@ -71,17 +78,18 @@ class htmlelements {
      * @param array $aAttributes
      * @return boolean
      */
-    protected function _setAttributes($aAttributes){
-        $this->_sLabel='';
-        if(isset($aAttributes['icon']) && $aAttributes['icon']){
-            $this->_sLabel.=$this->getIcon($aAttributes['icon']);
+    protected function _setAttributes(array $aAttributes): bool
+    {
+        $this->_sLabel = '';
+        if (isset($aAttributes['icon']) && $aAttributes['icon']) {
+            $this->_sLabel .= $this->getIcon($aAttributes['icon']);
             unset($aAttributes['icon']);
         }
-        if(isset($aAttributes['label']) && $aAttributes['label']){
+        if (isset($aAttributes['label']) && $aAttributes['label']) {
             $this->_sLabel .= $aAttributes['label'];
             unset($aAttributes['label']);
         }
-        $this->_aAttributes=$aAttributes;
+        $this->_aAttributes = $aAttributes;
         return true;
     }
 
@@ -91,21 +99,22 @@ class htmlelements {
     // HTML GENERIC
     // 
     // ----------------------------------------------------------------------
-    
+
     /**
-     * generic function to get html code for a single tag 
+     * Generic function to get html code for a single tag 
      * 
      * @param string   $sTag          tag name
      * @param array    $aAttributes   array with attributes (optional including 'icon' and 'label')
      * @param boolean  $bCloseTag     optional: set false if tag has no closing tag (= ending with "/>")
-     * @return type
+     * @return string html code
      */
-    public function getTag($sTag, $aAttributes, $bCloseTag=true){
+    public function getTag(string $sTag, array $aAttributes, bool $bCloseTag = true): string
+    {
         $sTpl = $bCloseTag ? "<$sTag%s>%s</$sTag>" : "<$sTag %s/>%s";
         $this->_setAttributes($aAttributes);
         return sprintf($sTpl, $this->_addAttributes(), $this->_sLabel);
     }
-    
+
     // ----------------------------------------------------------------------
     // 
     // PUBLIC FUNCTIONS
@@ -114,109 +123,22 @@ class htmlelements {
     // ----------------------------------------------------------------------
 
     /**
-     * helper detect prefix of a string add prefix of a framework
+     * Helper detect prefix of a string add prefix of a framework
      * i.e. value "fa-close" detects font awesome and adds "fa " as prefix
      * 
      * @param string $sIconclass
-     * @return boolean
+     * @return string HTML code
      */
-    public function getIcon($sIconclass=false){
-        if(!$sIconclass){
+    public function getIcon(string $sIconclass = ''): string
+    {
+        if (!$sIconclass) {
             return '';
         }
-        $sPrefix='';
-        foreach ($this->_aIcons as $sPrefix =>$add) {
-            if (strpos($sIconclass, $sPrefix)===0){
-                $sPrefix=$add;
-                continue;
-            }
-        }
-        // do not use this .. it overrides internal attribute vars
-        // return $this->getTag('i', array('class'=>$sPrefix.$sIconclass));
-        return '<i class="'.$sPrefix.$sIconclass.'"></i>&nbsp;&nbsp;';
-    }
-   
-
-    // ----------------------------------------------------------------------
-    // 
-    // PUBLIC FUNCTIONS
-    // HTML COMPONENTS
-    // 
-    // ----------------------------------------------------------------------
 
-    /**
-     * get html code for an input field
-     * 
-     * @param array $aAttributes  attributes of the select tag
-     * @return string
-     */
-    public function getFormInput($aAttributes){
-        $sTpl = '<input %s/>';
-        $this->_setAttributes($aAttributes);
-        return sprintf($sTpl, $this->_addAttributes());
-    }
-    /**
-     * get html code for an option field in a select drop down
-     * 
-     * @param array $aAttributes  attributes of the option tag
-     * @return string
-     */
-    public function getFormOption($aAttributes){
-        $sTpl = '<option %s>%s</option>';
-        $this->_setAttributes($aAttributes);
-        return sprintf($sTpl, $this->_addAttributes(), $this->_sLabel);
-    }
-    /**
-     * get html code for a select drop down
-     * 
-     * @param array $aAttributes  attributes of the select tag
-     * @param array $aOptions     array for all option fields
-     * @return string
-     */
-    public function getFormSelect($aAttributes, $aOptions=array()){
-        // $sTpl = '<select %s>%s</select>';
+        // do not use this .. it overrides internal attribute vars
+        // return $this->getTag('i', ['class'=>$sIconclass]);
 
-        if(!count($aOptions)){
-            return false;
-        }
-        $sOptions='';
-        foreach($aOptions as $aOptionAttributes){
-            // $sOptions.=$this->getFormOption($aOptionAttributes);
-            $sOptions.=$this->getTag('option', $aOptionAttributes);
-        }
-        $aAttributes['label']=$sOptions;
-        return $this->getTag('select', $aAttributes);
-        /*
-        $this->_setAttributes($aAttributes);
-        return sprintf($sTpl, $this->_addAttributes(), $sOptions);
-         * 
-         */
+        return "<i class=\"$sIconclass\"></i>&nbsp;&nbsp;";
     }
 
-    public function getTable($aHead, $aBody, $aTableAttributes=array()){
-        $sReturn='';
-        $sTdata='';
-        $sThead='';
-        $sTpl = '<table %s>'
-                . '<thead><tr>%s</tr></thead>'
-                . '<tbody>%s</tbody>'
-                . '</table>';
-        
-        foreach($aHead as $sTh){
-            $sThead.='<th>'.$sTh.'</th>';
-        }
-        foreach($aBody as $aTr){
-            $sTdata.='<tr>';
-            foreach($aTr as $sTd){
-                $sTdata.='<td>'.$sTd.'</td>';
-            }
-            $sTdata.='</tr>';
-        }
-        $this->_setAttributes($aTableAttributes);
-        return sprintf($sTpl, 
-                $this->_addAttributes(), 
-                $sThead,
-                $sTdata
-                );
-    }
 }
diff --git a/public_html/deployment/classes/htmlguielements.class.php b/public_html/deployment/classes/htmlguielements.class.php
index dd40f33658a98036f824c4f5027e4e2c909ba583..5e86bb78f059a8c2867c4a6cc81d16ab59744325 100644
--- a/public_html/deployment/classes/htmlguielements.class.php
+++ b/public_html/deployment/classes/htmlguielements.class.php
@@ -1,7 +1,8 @@
 <?php
 /**
  * html gui elements
- * for bootstrap 3
+ * for bootstrap 3..5
+ * 
  * CI SERVER GUI
  *
  * $oHtml=new htmlguielements();
@@ -9,305 +10,236 @@
  * echo $oHtml->getBox('error', 'errormessage');
  * echo $oHtml->getIcon('fa-pencil');
  * 
- * echo $oHtml->getLink(array(
+ * echo $oHtml->getLink([
  *     'href'=>'https://www.axel-hahn.de',
  *     'class'=>'btn btn-primary',
  *     'icon'=>'fa-close',
  *     'label'=>'linked text',
- * ));
+ * ]);
  * 
  * echo $oHtml->getTabs(
- *     array(
+ *     [
  *         'tab 1'=>'Inhalt #1',
  *         'tab 2'=>'Inhalt #2',
- *     )
+ *     ]
  * );
  * 
  * 
  * @author hahn
+ * 
+ * 2024-08-23  v1.1  Axel Hahn  php8 only; added variable types; short array syntax; remove unneeded methods
  */
-class htmlguielements{
-    
-    var $aCfg=array(
-        /*
-        'buttonsOLD'=>array(
-            // bootstrap defaults
-            'primary'=>array('class'=>'btn-primary', 'icon'=>''),
-            'success'=>array('class'=>'btn-success', 'icon'=>''),
-            'info'=>array('class'=>'btn-info', 'icon'=>''),
-            'warning'=>array('class'=>'btn-warning', 'icon'=>''),
-            'danger'=>array('class'=>'btn-danger', 'icon'=>''),
+class htmlguielements
+{
 
-            // custom buttons
-            'close'=>array('class'=>'btn-danger', 'icon'=>'fa-close'),
-            'error'=>array('class'=>'btn-danger', 'icon'=>'fa-bolt'),
-            'ok'=>array('class'=>'btn-primary', 'icon'=>'fa-check'),
-            
-            // deploy actions and buttons
-            'accept'=>array('class'=>''),
-            'build'=>array('class'=>''),
-            'cleanup'=>array('class'=>''),
-            'deploy'=>array('class'=>'', 'icon'=>'glyphicon-forward'),
-            'new'=>array('class'=>'', 'icon'=>'glyphicon-star-empty'),
-            'overview'=>array('class'=>''),
-            'phase'=>array('class'=>'', 'icon'=>'glyphicon-chevron-right'),
-            'rollback'=>array('class'=>'', 'icon'=>'glyphicon-forward'),
-            'setup'=>array('class'=>''),
-            
-        ),
-        'iconsOLD'=>array(
-
-            'menu'=>'fa-chevron-right',
-            'overview'=>'fa-list',
-            'project'=>'fa-book',
-            'project-home'=>'fa-home',
-            'projects'=>'fa-folder-o',
-            'actions'=>'fa-check',
-            
-            'actionlog'=>'fa-list-ul',
-            'accept'=>'glyphicon-forward',
-            'build'=>'glyphicon-equalizer',
-            'cleanup'=>'fa-trash',
-            'checklang'=>'fa-check',
-            'delete'=>'fa-close',
-            'deploy'=>'glyphicon-forward',
-            'filter'=>'glyphicon-filter',
-            'new'=>'glyphicon-star-empty',
-            'phase'=>'glyphicon-chevron-right',
-            'rollback'=>'glyphicon-forward',
-            'setup'=>'fa-cog',
-            'login'=>'fa-lock',
-            'user'=>'fa-user',
-            
-            'workflow'=>'fa-angle-double-right',
-            'repository'=>'fa-database',
-            'phase'=>'fa-flag',
-            'package'=>'fa-cubes',
-            'version'=>'fa-tag',
-            'list'=>'fa-list',
-            'raw-data'=>'fa-file-o',
-
-            'back'=>'fa-chevron-left',
-            
-            'branch'=>'glyphicon-bookmark',
-            'calendar'=>'glyphicon-calendar',
-            'comment'=>'glyphicon-comment',
-            'revision'=>'glyphicon-tag',
-
-            'link-extern'=>'glyphicon-globe',
-            
-            'host'=>'fa-hdd-o',
-            'hostgroup'=>'fa-sitemap',
-            'templatefile'=>'fa-file-code-o',
-            'targetfile'=>'fa-file-o',
-            'replace'=>'fa-random',
-            
-            'sign-info'=>'',
-            'sign-warning'=>'',
-            'sign-error'=>'fa-bolt',
-            'sign-ok'=>'',
-        ),
-         */
-        'buttons'=>array(
+    /**
+     * Configuration array with icons
+     * @var array
+     */
+    var $aCfg = [
+        'buttons' => [
             // bootstrap defaults
-            'primary'=>array('class'=>'btn-primary', 'icon'=>''),
-            'success'=>array('class'=>'btn-success', 'icon'=>''),
-            'info'=>array('class'=>'btn-info', 'icon'=>''),
-            'warning'=>array('class'=>'btn-warning', 'icon'=>''),
-            'danger'=>array('class'=>'btn-danger', 'icon'=>''),
+            'primary' => ['class' => 'btn-primary', 'icon' => ''],
+            'success' => ['class' => 'btn-success', 'icon' => ''],
+            'info' => ['class' => 'btn-info', 'icon' => ''],
+            'warning' => ['class' => 'btn-warning', 'icon' => ''],
+            'danger' => ['class' => 'btn-danger', 'icon' => ''],
 
             // custom buttons
-            'close'=>array('class'=>'btn-danger', 'icon'=>'fa-solid fa-times'),
-            'error'=>array('class'=>'btn-danger', 'icon'=>'fa-solid fa-bolt'),
-            'ok'=>array('class'=>'btn-primary', 'icon'=>'fa-solid fa-check'),
-            
+            'close' => ['class' => 'btn-danger', 'icon' => 'fa-solid fa-times'],
+            'error' => ['class' => 'btn-danger', 'icon' => 'fa-solid fa-bolt'],
+            'ok' => ['class' => 'btn-primary', 'icon' => 'fa-solid fa-check'],
+
             // deploy actions and buttons
-            'accept'=>array('class'=>''),
-            'build'=>array('class'=>''),
-            'cleanup'=>array('class'=>''),
-            'deploy'=>array('class'=>'', 'icon'=>'fa-solid fa-forward'),
-            'new'=>array('class'=>'', 'icon'=>'fa-regular fa-star'),
-            'overview'=>array('class'=>''),
-            'phase'=>array('class'=>'', 'icon'=>'fa-solid fa-chevron-right'),
-            'rollback'=>array('class'=>'', 'icon'=>'fa-solid fa-forward'),
-            'setup'=>array('class'=>''),
-            
-        ),
-        'icons'=>array(
-
-            'menu'=>'fa-solid fa-chevron-right',
-            'valuestore'=>'fa-solid fa-tags',
-            'overview'=>'fa-solid fa-list',
-            'project'=>'fa-solid fa-book',
-            'project-home'=>'fa-solid fa-home',
-            'projects'=>'fa-regular fa-folder',
-            'actions'=>'fa-solid fa-check',
-            
-            'actionlog'=>'fa-solid fa-list-ul',
+            'accept' => ['class' => ''],
+            'build' => ['class' => ''],
+            'cleanup' => ['class' => ''],
+            'deploy' => ['class' => '', 'icon' => 'fa-solid fa-forward'],
+            'new' => ['class' => '', 'icon' => 'fa-regular fa-star'],
+            'overview' => ['class' => ''],
+            'phase' => ['class' => '', 'icon' => 'fa-solid fa-chevron-right'],
+            'rollback' => ['class' => '', 'icon' => 'fa-solid fa-forward'],
+            'setup' => ['class' => ''],
+
+        ],
+        'icons' => [
+
+            'menu' => 'fa-solid fa-chevron-right',
+            'valuestore' => 'fa-solid fa-tags',
+            'overview' => 'fa-solid fa-list',
+            'project' => 'fa-solid fa-book',
+            'project-home' => 'fa-solid fa-home',
+            'projects' => 'fa-regular fa-folder',
+            'actions' => 'fa-solid fa-check',
+
+            'actionlog' => 'fa-solid fa-list-ul',
             // 'accept'=>'fa-solid fa-forward',
-            'accept'=>'fa-solid fa-check',
-            'build'=>'fa-solid fa-box-open',
-            'checklang'=>'fa-solid fa-check',
-            'cleanup'=>'fa-solid fa-broom',
-            'close'=>'fa-solid fa-times',
-            'delete'=>'fa-solid fa-trash',
-            'deploy'=>'fa-solid fa-forward',
-            'deploy-configfile'=>'fa-regular fa-file-code',
-            'deploy-rollout-plugin'=>'fa-solid fa-plug',
-            'filter'=>'fa-solid fa-filter',
-            'foreman'=>'fa-solid fa-hard-hat',
-            'gotop'=>'fa-solid fa-arrow-up',
-            'help'=>'fa-solid fa-life-ring',
-            'login'=>'fa-solid fa-right-to-bracket',
-            'new'=>'fa-regular fa-star',
-            'phase'=>'fa-solid fa-chevron-right',
-            'poweroff'=>'fa-solid fa-power-off',
-            'refresh'=>'fa-solid fa-sync',
-            'rollback'=>'fa-solid fa-forward',
-            'setup'=>'fa-solid fa-cog',
-            'time'=>'fa-regular fa-clock',
-            'waiting'=>'fa-solid fa-clock',
-            'user'=>'fa-solid fa-user',
-            'user-profile'=>'fa-regular fa-id-card',
-            'user-group'=>'fa-regular fa-bookmark',
-            'user-permission'=>'fa-solid fa-caret-right',
-            
-            'workflow'=>'fa-solid fa-angle-double-right',
-            'repository'=>'fa-solid fa-database',
-            'phase'=>'fa-solid fa-flag',
-            'package'=>'fa-solid fa-cubes',
-            'version'=>'fa-solid fa-tag',
-            'list'=>'fa-solid fa-list',
-            'raw-data'=>'fa-regular fa-file',
-            'method'=>'fa-solid fa-cogs',
-            'url'=>'fa-solid fa-globe-americas',
-
-            'back'=>'fa-solid fa-chevron-left',
-            
-            'branch'=>'fa-solid fa-bookmark',
-            'calendar'=>'fa-regular fa-calendar',
-            'comment'=>'fa-regular fa-comment',
-            'revision'=>'fa-solid fa-tag',
-
-            'link-extern'=>'fa-solid fa-globe-americas',
-            
-            'host'=>'fa-regular fa-hdd',
-            'hostgroup'=>'fa-solid fa-sitemap',
-            'file-any'=>'fa-regular fa-file',
-            'file-archive'=>'fa-regular fa-file-archive',
-            'file-code'=>'fa-regular fa-file-code',
-            'file-meta'=>'fa-regular fa-file',
-            'file-template'=>'fa-regular fa-file-alt',
-            'file-target'=>'fa-solid fa-file-upload',
-            'replace'=>'fa-solid fa-random',
-            
-            'box-up'=>'fa-chevron-up',
-            'box-down'=>'fa-chevron-down',
-
-            'sign-info'=>'fa-solid fa-info',
-            'sign-warning'=>'fa-solid fa-exclamation',
-            'sign-error'=>'fa-solid fa-bolt',
-            'sign-ok'=>'fa-solid fa-check',
-            'sign-success'=>'fa-solid fa-check',
-        ),
-    );
-    
-    public function __construct() {
-        return true;
+            'accept' => 'fa-solid fa-check',
+            'build' => 'fa-solid fa-box-open',
+            'checklang' => 'fa-solid fa-check',
+            'cleanup' => 'fa-solid fa-broom',
+            'close' => 'fa-solid fa-times',
+            'delete' => 'fa-solid fa-trash',
+            'deploy' => 'fa-solid fa-forward',
+            'deploy-configfile' => 'fa-regular fa-file-code',
+            'deploy-rollout-plugin' => 'fa-solid fa-plug',
+            'filter' => 'fa-solid fa-filter',
+            'foreman' => 'fa-solid fa-hard-hat',
+            'gotop' => 'fa-solid fa-arrow-up',
+            'help' => 'fa-solid fa-life-ring',
+            'login' => 'fa-solid fa-right-to-bracket',
+            'new' => 'fa-regular fa-star',
+            // 'phase' => 'fa-solid fa-chevron-right',
+            'poweroff' => 'fa-solid fa-power-off',
+            'refresh' => 'fa-solid fa-sync',
+            'rollback' => 'fa-solid fa-forward',
+            'setup' => 'fa-solid fa-cog',
+            'time' => 'fa-regular fa-clock',
+            'waiting' => 'fa-solid fa-clock',
+            'user' => 'fa-solid fa-user',
+            'user-profile' => 'fa-regular fa-id-card',
+            'user-group' => 'fa-regular fa-bookmark',
+            'user-permission' => 'fa-solid fa-caret-right',
+
+            'workflow' => 'fa-solid fa-angle-double-right',
+            'repository' => 'fa-solid fa-database',
+            'phase' => 'fa-solid fa-flag',
+            'package' => 'fa-solid fa-cubes',
+            'version' => 'fa-solid fa-tag',
+            'list' => 'fa-solid fa-list',
+            'raw-data' => 'fa-regular fa-file',
+            'method' => 'fa-solid fa-cogs',
+            'url' => 'fa-solid fa-globe-americas',
+
+            'back' => 'fa-solid fa-chevron-left',
+
+            'branch' => 'fa-solid fa-bookmark',
+            'calendar' => 'fa-regular fa-calendar',
+            'comment' => 'fa-regular fa-comment',
+            'revision' => 'fa-solid fa-tag',
+
+            'link-extern' => 'fa-solid fa-globe-americas',
+
+            'host' => 'fa-regular fa-hdd',
+            'hostgroup' => 'fa-solid fa-sitemap',
+            'file-any' => 'fa-regular fa-file',
+            'file-archive' => 'fa-regular fa-file-archive',
+            'file-code' => 'fa-regular fa-file-code',
+            'file-meta' => 'fa-regular fa-file',
+            'file-template' => 'fa-regular fa-file-alt',
+            'file-target' => 'fa-solid fa-file-upload',
+            'replace' => 'fa-solid fa-random',
+
+            'box-up' => 'fa-solid fa-chevron-up',
+            'box-down' => 'fa-solid fa-chevron-down',
+
+            'sign-info' => 'fa-solid fa-info',
+            'sign-warning' => 'fa-solid fa-exclamation',
+            'sign-error' => 'fa-solid fa-bolt',
+            'sign-ok' => 'fa-solid fa-check',
+            'sign-success' => 'fa-solid fa-check',
+        ],
+    ];
+
+    /**
+     * Constructor
+     */
+    public function __construct()
+    {
+        // nothing here
     }
-    
+
     // ----------------------------------------------------------------------
     // helper function
     // ----------------------------------------------------------------------
-    
+
     /**
-     * add an html attribute if the attribute exists as a key
+     * Add an html attribute if the attribute exists as a key
      * @param string  $sAttribute  html attribute to add
-     * @param string  $aData       item array
+     * @param array  $aData       item array
      * @param string  $sDefault    use default if key does not exists
      * @return string
      */
-    public function addAttributeFromKey($sAttribute, $aData, $sDefault=''){
-        return (isset($aData[$sAttribute]) 
-                ? $this->addAttribute($sAttribute, $aData[$sAttribute])
-                : $this->addAttribute($sAttribute, $sDefault)
-                );
+    public function addAttributeFromKey(string $sAttribute, array $aData, string $sDefault = ''): string
+    {
+        return (isset($aData[$sAttribute])
+            ? $this->addAttribute($sAttribute, $aData[$sAttribute])
+            : $this->addAttribute($sAttribute, $sDefault)
+        );
     }
-    
+
     /**
-     * add an html attribute if value is not empty
+     * Add an html attribute if value is not empty
      * @param string  $sAttribute  html attribute to add
      * @param string  $sValue      value of attribute
      * @return string
      */
-    public function addAttribute($sAttribute, $sValue){
-        return ($sValue ? ' '.$sAttribute.'="'.$sValue.'"' : '' );
+    public function addAttribute(string $sAttribute, string $sValue): string
+    {
+        return ($sValue ? ' ' . $sAttribute . '="' . $sValue . '"' : '');
     }
-    
+
     /**
-     * get html attributes as string from all keys of given hash
+     * Get html attributes as string from all keys of given hash
      * 
      * @param array $aItem
      * @return string
      */
-    public function addAllAttributes($aItem){
-        $sReturn='';
-        foreach (array_keys($aItem) as $sKey){
-            $sReturn.=$this->addAttributeFromKey($sKey, $aItem);
+    public function addAllAttributes(array $aItem): string
+    {
+        $sReturn = '';
+        foreach (array_keys($aItem) as $sKey) {
+            $sReturn .= $this->addAttributeFromKey($sKey, $aItem);
         }
         return $sReturn;
     }
-    
+
     // ----------------------------------------------------------------------
     // low level
     // ----------------------------------------------------------------------
-    
+
     /**
-     * get html code for icon; glypphicons and font-awesome is supported
+     * Get html code for icon; glypphicons and font-awesome is supported
      * 
      * @param string $sLabel  label of icon
      * @return string
      */
-    public function getIcon($sLabel){
-        if(!$sLabel){
+    public function getIcon(string $sLabel): string
+    {
+        if (!$sLabel) {
             return '';
         }
-        $sPrefix=(
-                strpos($sLabel, 'glyphicon-')===0 ? 'glyphicon' 
-                : ( strpos($sLabel, 'fa-')===0 ? 'fa' : '')
-                );
-        // if(!$sPrefix){
-        if(isset($this->aCfg['icons'][$sLabel])){
+        if (isset($this->aCfg['icons'][$sLabel])) {
             return $this->getIconByType($sLabel);
         }
-        return '<i'.$this->addAttribute('class', ($sPrefix ? $sPrefix . ' ' : '').$sLabel).'></i> ';
+        return '<i' . $this->addAttribute('class', $sLabel) . '></i> ';
     }
+
     /**
-     * get html code for icon; glypphicons and font-awesome is supported
+     * Get html code for icon
      * 
      * @param string $sLabel  label of icon
      * @return string
      */
-    public function getIconClass($sLabel){
-        if(!$sLabel){
+    public function getIconClass(string $sLabel): string
+    {
+        if (!$sLabel) {
             return '';
         }
-        if(isset($this->aCfg['icons'][$sLabel])){
+        if (isset($this->aCfg['icons'][$sLabel])) {
             return $this->aCfg['icons'][$sLabel];
         }
-        $sPrefix=(
-                strpos($sLabel, 'glyphicon-')===0 ? 'glyphicon' 
-                : ( strpos($sLabel, 'fa-')===0 ? 'fa' : '')
-                );
-        return ($sPrefix ? $sPrefix . ' ' : '').$sLabel;
+        return $sLabel;
     }
-    
+
     /**
-     * get a default icon from config
+     * Get a default icon from config
+     * 
      * @param string  $sType  icon type
-     * @return array 
+     * @return string
      */
-    public function getIconByType($sType){
+    public function getIconByType(string $sType): string
+    {
         return (isset($this->aCfg['icons'][$sType])
             ? $this->getIcon($this->aCfg['icons'][$sType])
             : ''
@@ -315,53 +247,55 @@ class htmlguielements{
     }
 
     /**
-     * get html code for icon; glypphicons and font-awesome is supported
+     * Get html code for icon; glypphicons and font-awesome is supported
      * 
      * @param array $aItem  array with link attributes; href for target; "label" and "icon" 
      * @return string
      */
-    public function getLink($aItem){
-        
-        $sHref=$this->addAttributeFromKey('href', $aItem, '#');
-        $sLabel=(isset($aItem['icon']) ? $this->getIcon($aItem['icon']): '')
-            .(isset($aItem['label']) ? $aItem['label'] : '');
-        
-        foreach(array('href', 'icon', 'label') as $sKey){
-            if (isset($aItem[$sKey])){
+    public function getLink(array $aItem): string
+    {
+
+        $sHref = $this->addAttributeFromKey('href', $aItem, '#');
+        $sLabel = (isset($aItem['icon']) ? $this->getIcon($aItem['icon']) : '')
+            . (isset($aItem['label']) ? $aItem['label'] : '');
+
+        foreach (['href', 'icon', 'label'] as $sKey) {
+            if (isset($aItem[$sKey])) {
                 unset($aItem[$sKey]);
             }
         }
-        
-        $sReturn='<a'.$sHref;
-        $sReturn.=$this->addAllAttributes($aItem);
-        $sReturn.='>'
-                .$sLabel
-                .'</a>';
+
+        $sReturn = '<a' . $sHref;
+        $sReturn .= $this->addAllAttributes($aItem);
+        $sReturn .= '>'
+            . $sLabel
+            . '</a>';
         return $sReturn;
     }
-    
+
     /**
-     * add default css classes and colors based on $aItem['type'] and the
+     * Add default css classes and colors based on $aItem['type'] and the
      * local default settings in $this->aCfg
      * 
      * @param array  $aItem
      * @return array 
      */
-    protected function _getButtonattributesByType($aItem){
-        $aReturn=$aItem;
-        if (isset($this->aCfg['buttons'][$aItem['type']])){
-            $sClass=$this->aCfg['buttons'][$aItem['type']]['class'];
-            $aReturn['class'].=$sClass ? ' '.$sClass : '';
-            
+    protected function _getButtonattributesByType(array $aItem): array
+    {
+        $aReturn = $aItem;
+        if (isset($this->aCfg['buttons'][$aItem['type']])) {
+            $sClass = $this->aCfg['buttons'][$aItem['type']]['class'];
+            $aReturn['class'] .= $sClass ? ' ' . $sClass : '';
+
             // icon priority:
             // given in param --> icon in button config --> icon in icon config
-            $aReturn['icon']=$aReturn['icon'] ? $aReturn['icon'] : 
-                ( $this->aCfg['buttons'][$aItem['type']]['icon'] 
-                    ? $this->aCfg['buttons'][$aItem['type']]['icon'] 
-                    : ( 
-                        isset($this->aCfg['icons'][$aItem['type']]) 
-                            ? $this->aCfg['icons'][$aItem['type']] 
-                            : ''
+            $aReturn['icon'] = $aReturn['icon'] ? $aReturn['icon'] :
+                ($this->aCfg['buttons'][$aItem['type']]['icon']
+                    ? $this->aCfg['buttons'][$aItem['type']]['icon']
+                    : (
+                        isset($this->aCfg['icons'][$aItem['type']])
+                        ? $this->aCfg['icons'][$aItem['type']]
+                        : ''
                     )
                 );
         }
@@ -370,28 +304,29 @@ class htmlguielements{
 
 
     /**
-     * get html code for icon; glypphicons and font-awesome is supported
+     * Get html code for a button like link
      * 
      * @param array $aItem  array with link attributes; href for target; "label" and "icon" 
      * @return string
      */
-    public function getLinkButton($aItem){
-        foreach(array('class', 'icon') as $sKey){
-            if (!isset($aItem[$sKey])){
-                $aItem[$sKey]='';
+    public function getLinkButton($aItem)
+    {
+        foreach (['class', 'icon'] as $sKey) {
+            if (!isset($aItem[$sKey])) {
+                $aItem[$sKey] = '';
             }
         }
-        
-        if (isset($aItem['type'])){
-            $aItem=$this->_getButtonattributesByType($aItem);
+
+        if (isset($aItem['type'])) {
+            $aItem = $this->_getButtonattributesByType($aItem);
             unset($aItem['type']);
         }
         // if not class "btn" was added: add "btn" 
         // if not class "btn-[something]" was added: add "btn-default" 
-        $sClass=$aItem['class'];
-        $sClass=(strstr($sClass, 'btn-') ? '' : 'btn-default ').$sClass;
-        $sClass=(strstr($sClass, 'btn ') ? '' : 'btn ').$sClass;
-        $aItem['class']=$sClass;
+        $sClass = $aItem['class'];
+        $sClass = (strstr($sClass, 'btn-') ? '' : 'btn-default ') . $sClass;
+        $sClass = (strstr($sClass, 'btn ') ? '' : 'btn ') . $sClass;
+        $aItem['class'] = $sClass;
 
         // $aItem['label'].=' -> '.$sClass;
         return $this->getLink($aItem);
@@ -402,71 +337,26 @@ class htmlguielements{
     // ----------------------------------------------------------------------
 
     /**
-     * get html code of a div around a message
+     * Get html code of a div around a message
+     * 
      * @param string $sWarnlevel one of error|success|info|warning to get a colored box
      * @param string $sMessage   message text
      * @return string
      */
-    public function getBox($sWarnlevel, $sMessage) {
-        $aCfg = array(
-            "error" => array("class" => "alert alert-danger", "prefix" => t("error")),
-            "success" => array("class" => "alert alert-success", "prefix" => t("success")),
-            "info" => array("class" => "alert alert-info", "prefix" => t("info")),
-            "warning" => array("class" => "alert alert-warning", "prefix" => t("warning")),
-        );
+    public function getBox(string $sWarnlevel, string $sMessage): string
+    {
+        $aCfg = [
+            "error" => ["class" => "alert alert-danger", "prefix" => t("error")],
+            "success" => ["class" => "alert alert-success", "prefix" => t("success")],
+            "info" => ["class" => "alert alert-info", "prefix" => t("info")],
+            "warning" => ["class" => "alert alert-warning", "prefix" => t("warning")],
+            ];
         $sClass = "";
         if (isset($aCfg[$sWarnlevel])) {
             $sClass = $aCfg[$sWarnlevel]["class"];
-            $sMessage = '<strong>' . $this->getIcon('sign-'.$sWarnlevel).$aCfg[$sWarnlevel]["prefix"] . '</strong> ' . $sMessage;
+            $sMessage = '<strong>' . $this->getIcon('sign-' . $sWarnlevel) . $aCfg[$sWarnlevel]["prefix"] . '</strong> ' . $sMessage;
         }
-        return '<div'.$this->addAttribute('class', $sClass).'>' . $sMessage . '</div>';
-    }
-    
-    /**
-     * get html code for tabs with content
-     * 
-     * @staticvar int $iCounter  internal counter for tabs ans content
-     * @param array  $aTabData  tab data; key is the tab label; value the content of its tab
-     * @return string
-     */
-    public function getNav__UNUSED($aTabData){
-        $sTabs='';
-        $sContent='';
-        
-        static $iCounter=0;
-        $iTab=0;
-        
-        if (!is_array($aTabData) || !count($aTabData)){
-            return false;
-        }
-        $sNavType=$aTabData['options']['type']; // "tabs" or "pills"
-        $sNavCss='nav nav-'.$sNavType;
-        if (isset($aTabData['options']['stacked']) && $aTabData['options']['stacked']){
-            $sNavCss.=' nav-stacked';
-        }
-        if (isset($aTabData['options']['justified']) && $aTabData['options']['justified']){
-            $sNavCss.=' nav-justified';
-        }
-        $sNavType=$aTabData['options']['justified'];
-        foreach ($aTabData['tabs'] as  $sTabLabel=>$sTabContent){
-            $iCounter++;
-            $iTab++;
-            $sId="tab-generated-$iCounter";
-            $sTabs.= ($iTab==1 ?  '<li class="active"' : '<li')
-                . ' role="presentation">'
-                . '<a href="#'.$sId.'" data-toggle="tab">' . $sTabLabel . '</a></li>'
-                ;
-            $sContent.='<div class="tab-pane'
-                    .($iTab==1 ?  ' active' : '')
-                    .'" id="'.$sId.'">'
-                    .$sTabContent
-                    .'</div>'
-                    ;
-        }
-        return '<div class="tabbable">'
-                . '<ul class="'.$sNavCss.'">'. $sTabs.'</ul>'
-                . '<div class="tab-content">'.$sContent.'</div>'
-                . '</div>';
+        return '<div' . $this->addAttribute('class', $sClass) . '>' . $sMessage . '</div>';
     }
 
     /**
@@ -475,28 +365,29 @@ class htmlguielements{
      * @param array $aTabledata  array with subkeys "header" and "body"
      * @return string
      */
-    public function getTable($aTabledata) {
-        $sTHead='';
-        $sTBody='';
-        if (isset($aTabledata['body'])){
-            foreach ($aTabledata['body'] as $aRow){
-                $sTBody.='<tr>';
-                foreach ($aRow as $sItem){
-                    $sTBody.='<td>'.$sItem.'</td>';
+    public function getTable(array $aTabledata): string
+    {
+        $sTHead = '';
+        $sTBody = '';
+        if (isset($aTabledata['body'])) {
+            foreach ($aTabledata['body'] as $aRow) {
+                $sTBody .= '<tr>';
+                foreach ($aRow as $sItem) {
+                    $sTBody .= '<td>' . $sItem . '</td>';
                 }
-                $sTBody.='</tr>';
+                $sTBody .= '</tr>';
             }
         }
-        if (isset($aTabledata['header'])){
-            foreach ($aTabledata['header'] as $sItem){
-                $sTHead.='<th>'.$sItem.'</th>';
+        if (isset($aTabledata['header'])) {
+            foreach ($aTabledata['header'] as $sItem) {
+                $sTHead .= '<th>' . $sItem . '</th>';
             }
         }
         return '<table class="table" style="width: auto;">'
-            .($sTHead ? '<thead>'.$sTHead.'</thead>' : '')
-            .($sTBody ? '<tbody>'.$sTBody.'</tbody>' : '')
-            .'</table>'
-            ;
+            . ($sTHead ? '<thead>' . $sTHead . '</thead>' : '')
+            . ($sTBody ? '<tbody>' . $sTBody . '</tbody>' : '')
+            . '</table>'
+        ;
     }
-        
+
 }
diff --git a/public_html/deployment/classes/ldap.class.php b/public_html/deployment/classes/ldap.class.php
index 82e4a57c387cfced3423599f0cb00f6763289b5d..a4d4de90f463c67e2322f375f2d9a3333bf56299 100644
--- a/public_html/deployment/classes/ldap.class.php
+++ b/public_html/deployment/classes/ldap.class.php
@@ -3,53 +3,66 @@
 /**
  * 
  * IML LDAP CONNECTOR
- *<pre>
- * 2022-02-22  ah  added objGet(), sanitizeFilter() <br>
- * 2022-08-18  ah  mask password (showing 4 chars only) <br>
- * 2022-08-22  ah  mhash is deprecated <br>
- * 2022-08-26  ah  fix verifyPassword <br>
- * </pre>
- * @author axel.hahn@iml.unibe.ch
+ *
+ * @author axel.hahn@unibe.ch
+ * @license GNU GPL v3
+ *
+ * SOURCE: <https://git-repo.iml.unibe.ch/iml-open-source/ldap-php-class/>
+ * DOCS: <https://os-docs.iml.unibe.ch/ldap-php-class/index.html>
+ * 
+ * 2022-02-22  ah  added objGet(), sanitizeFilter()
+ * 2022-08-18  ah  mask password (showing 4 chars only)
+ * 2022-08-22  ah  mhash is deprecated
+ * 2022-08-26  ah  fix verifyPassword
+ * 2024-07-11  ah  php8 only: use variable types; update phpdocs
+ * 2024-07-12  ah  remove connection port (use server value "ldaps://<host>:<port>" if needed) 
  */
-class imlldap {
+class imlldap
+{
 
     // ----------------------------------------------------------------------
     // vars
     // ----------------------------------------------------------------------
-    
+
     /**
      * @var array  options array for an ldap connection including some base settings and DNs
      */
-    private $_aLdap = array(
+    private array $_aLdap = [
         'server' => false,
-        'port' => false,
         'DnLdapUser' => false, // ldap rdn oder dn
         'PwLdapUser' => false,
         'DnUserNode' => false, // ou=People...
         'DnAppNode' => false, // cn=AppGroup...
         'protoVersion' => 3,
         'debugLevel' => 0,
-    );
+    ];
     /**
      * @var object  current ldap connection  
      */
-    private $_ldapConn = false;
+    private object|bool $_ldapConn = false;
+
+    /**
+     * ldap bind object - bind was done?
+     * @var object|bool
+     */
+    private object|bool $_ldapBind = false;
 
     /**
-     * @var bool  bind was done?
+     * Flag if debug mode is on
+     * @var bool
      */
-    private $_ldapBind = false;
-    var $bDebug = false;
+    var bool $bDebug = false;
 
     // ----------------------------------------------------------------------
     // functions
     // ----------------------------------------------------------------------
-    
+
     /**
      * constructor
      * @param array  $aConfig  optional set ldap connection
      */
-    public function __construct($aConfig = array()) {
+    public function __construct(array $aConfig = [])
+    {
         if (!function_exists("ldap_connect")) {
             die(__CLASS__ . " ERROR: php-ldap module is not installed on this server.");
         }
@@ -58,7 +71,8 @@ class imlldap {
         }
     }
 
-    public function __destruct() {
+    public function __destruct()
+    {
         $this->close();
     }
 
@@ -72,7 +86,8 @@ class imlldap {
      * ldap config array
      * @see setConfig()
      */
-    public function debugOn() {
+    public function debugOn(): void
+    {
         $this->bDebug = true;
         if ($this->_aLdap['debugLevel']) {
             $this->_w(__FUNCTION__ . ' setting debug level ' . $this->_aLdap['debugLevel']);
@@ -83,7 +98,8 @@ class imlldap {
     /**
      * turn debug messages off
      */
-    public function debugOff() {
+    public function debugOff(): void
+    {
         $this->bDebug = false;
         ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 0);
     }
@@ -94,7 +110,8 @@ class imlldap {
      * @param string  $sText  message text
      * @return boolean
      */
-    private function _w($sText) {
+    private function _w(string $sText): bool
+    {
         if (!$this->bDebug) {
             return false;
         }
@@ -108,8 +125,9 @@ class imlldap {
      * @param string  $sText  message text
      * @return boolean
      */
-    private function _wLdaperror($sText = '') {
-        $this->_w(($sText ? $sText . ' - ' : '' ) . 'last LDAP-ERROR: ' . ldap_error($this->_ldapConn));
+    private function _wLdaperror(string $sText = ''): bool
+    {
+        $this->_w(($sText ? $sText . ' - ' : '') . 'last LDAP-ERROR: ' . ldap_error($this->_ldapConn));
         return true;
     }
 
@@ -118,19 +136,18 @@ class imlldap {
     // ----------------------------------------------------------------------
 
     /**
-     * set a ldap config 
+     * set a ldap config or modify existing value
      * 
-     * @param array  $aConfig   new config items
-     *             'server'       => 'ldaps://ldap.example.com',
-     *             'port'         => 636,
-     *             'DnLdapUser' => 'cn=Lookup,ou=ServiceAccounts,dc=org,dc=example.com',     // ldap rdn oder dn
-     *             'PwLdapUser' => 'IkHEFFzlZ...99j0h8WdI0LrLhxU',  // password
-     *             'DnUserNode'   => 'ou=People,ou=ORG,dc=org,dc=example.com',
-     *             'DnAppNode'    => '' optional dn ... if a user must be member of a given group
-     *             'protoVersion' => 3
-     *             'debugLevel'   => 0 // for debugging set higher 0 AND call debugOn()
-     */
-    public function setConfig($aConfig = array()) {
+     * @param array  $aConfig   new config items with these keys
+     *               'server'       => 'ldaps://ldap.example.com',
+     *               'DnLdapUser'   => 'cn=Lookup,ou=ServiceAccounts,dc=org,dc=example.com', // ldap rdn oder dn
+     *               'PwLdapUser'   => 'PasswordOfLookupUser',                               // password
+     *               'DnUserNode'   => 'ou=People,ou=ORG,dc=org,dc=example.com',
+     *               'protoVersion' => 3
+     *               'debugLevel'   => 0 // value for LDAP_OPT_DEBUG_LEVEL in debugOn()
+     */
+    public function setConfig(array $aConfig = []): void
+    {
         if (is_array($aConfig)) {
             foreach (array_keys($this->_aLdap) as $sKey) {
                 if (array_key_exists($sKey, $aConfig)) {
@@ -148,7 +165,8 @@ class imlldap {
     /**
      * close an existing ldap connection
      */
-    public function close() {
+    public function close(): void
+    {
         if ($this->_ldapConn) {
             $this->_w(__FUNCTION__ . ' closing connection.');
             ldap_close($this->_ldapConn);
@@ -162,7 +180,8 @@ class imlldap {
     /**
      * connect to ldap
      */
-    public function connect() {
+    public function connect(): void
+    {
 
         if (!array_key_exists('server', $this->_aLdap) || !$this->_aLdap['server']) {
             die(__CLASS__ . " ERROR: no ldap server was setup set. Use setConfig() first.");
@@ -172,8 +191,8 @@ class imlldap {
             $this->close();
         }
 
-        $this->_w(__FUNCTION__ . ' connect to ' . $this->_aLdap['server'] . ':' . $this->_aLdap['port']);
-        $this->_ldapConn = ldap_connect($this->_aLdap['server'], $this->_aLdap['port']);
+        $this->_w(__FUNCTION__ . ' connect to ' . $this->_aLdap['server']);
+        $this->_ldapConn = ldap_connect($this->_aLdap['server']);
         if (!$this->_ldapConn) {
             $this->_wLdaperror(__FUNCTION__);
             die(__CLASS__ . " ERROR: ldap connect failed.");
@@ -201,10 +220,11 @@ class imlldap {
      * @param string  $sUser   optional: username (overrides _aLdap['DnLdapUser'])
      * @param string  $sPw     optional: password (overrides _aLdap['PwLdapUser'])
      */
-    public function bind($sUser = '', $sPw = '') {
-        if(!$sUser){
+    public function bind(string $sUser = '', string $sPw = ''): bool
+    {
+        if (!$sUser) {
             $sUser = $this->_aLdap['DnLdapUser'];
-            $sPw   = $this->_aLdap['PwLdapUser'];
+            $sPw = $this->_aLdap['PwLdapUser'];
         }
 
         if (!$this->_ldapConn) {
@@ -218,7 +238,7 @@ class imlldap {
             $this->_w(__FUNCTION__ . ' ERROR: no user was set as first param.');
             die("ERROR: no user was given to connect to ldap.");
         }
-        $this->_w(__FUNCTION__ . ' with user ' . $sUser . ' PW ' . substr($sPw,0,4).'**********');
+        $this->_w(__FUNCTION__ . ' with user ' . $sUser . ' PW ' . substr($sPw, 0, 4) . '**********');
 
         $this->_ldapBind = @ldap_bind($this->_ldapConn, $sUser, $sPw);
         if (!$this->_ldapBind) {
@@ -232,7 +252,8 @@ class imlldap {
     /**
      * ldap unbind ... if a bind exists
      */
-    public function unbind() {
+    public function unbind(): void
+    {
         if ($this->_ldapBind && !is_bool($this->_ldapBind)) {
             $this->_w(__FUNCTION__ . ' ...');
             ldap_unbind($this->_ldapBind);
@@ -251,30 +272,33 @@ class imlldap {
      * @param string  $sDn  DN to check
      * @return boolean
      */
-    public function DnExists($sDn) {
-        $aData = $this->searchDn($sDn, '(&(objectclass=top))', array("*"));
+    public function DnExists(string $sDn): bool
+    {
+        $aData = $this->searchDn($sDn, '(&(objectclass=top))', ["*"]);
         return is_array($aData);
     }
 
     /**
      * get simpler array from ldap_get_entries after ldap_search
+     * If the given array doesn't contain the key "dn" it returns "false"
      * 
-     * @param array  $aRecord  singel result item
+     * @param array  $aRecord  single result item
      * @return array
      */
-    public function normalizeSearchentry($aRecord) {
-        if (!is_array($aRecord) || !isset($aRecord['dn'])){
+    public function normalizeSearchentry(array $aRecord): bool|array
+    {
+        if (!is_array($aRecord) || !isset($aRecord['dn'])) {
             return false;
         }
-        $aItem = array();
+        $aItem = [];
         unset($aRecord['count']);
         foreach ($aRecord as $sAttr => $aData) {
             if (!is_integer($sAttr)) {
                 $value = $aData;
                 if (is_array($aData)) {
                     unset($aData['count']);
-                    $bUseArray=count($aData)>1 || array_search($sAttr, array('hieradata', 'member', 'memberof', 'objectclass'))!==false;
-                    if($bUseArray){
+                    $bUseArray = count($aData) > 1 || array_search($sAttr, ['hieradata', 'member', 'memberof', 'objectclass']) !== false;
+                    if ($bUseArray) {
                         sort($aData);
                     }
                     $value = $bUseArray ? $aData : $aData[0];
@@ -284,23 +308,6 @@ class imlldap {
         }
         return $aItem;
     }
-    /**
-     * get simpler array from ldap_get_entries after ldap_search
-     * 
-     * @param array  $aRecord  singel result item
-     * @return array
-     */
-    public function normalizeSearchresult($aLdapSearchresult) {
-        if (!is_array($aLdapSearchresult)){
-            return false;
-        }
-        $aReturn = array();
-        unset($aRecord['count']);
-        foreach ($aLdapSearchresult as $aRecord) {
-            $aReturn[]=$this->normalizeSearchentry($aRecord);
-        }
-        return $aReturn;
-    }
 
     /**
      * sanitize value to put into a search filter
@@ -315,51 +322,56 @@ class imlldap {
      * @param  string   $s  value to sanitize
      * @return string
      */
-    static public function sanitizeFilter($s){
+    static public function sanitizeFilter(string $s): string
+    {
 
         // helper array to replace special chars
-        $aReplace=array();
-        for($i=0; $i<65; $i++){
-            $val=dechex($i);
-            if ($val<10){
-                $val="0$val";
+        $aReplace = [];
+        for ($i = 0; $i < 65; $i++) {
+            $val = dechex($i);
+            if ($val < 10) {
+                $val = "0$val";
             }
-            $aReplace[chr($i)]='\\'.$val;
+            $aReplace[chr($i)] = '\\' . $val;
         }
 
-        $sReturn=$s;
-        $sReturn=str_replace(array_keys($aReplace), array_values($aReplace), $sReturn);
-        
+        $sReturn = $s;
+        $sReturn = str_replace(array_keys($aReplace), array_values($aReplace), $sReturn);
+
         return $sReturn;
     }
     /**
-     * search in ldap directory and get result as array
+     * search in ldap directory and get result as array.
+     * It returns "false" on error:
+     * - no ldap connection
+     * - search failed
      * 
      * @param string  $sDn               DN to search for
      * @param string  $sSearchFilter     filter in ldap filter syntax
      * @param array   $aAttributesToGet  flat array of attributes to fetch
      * @param boolean $bRecursive        recusrive (uses ldap_search) or not (ldap_list)
-     * @return array
+     * @return boolean|array
      */
-    public function searchDn($sDn, $sSearchFilter='(objectclass=*)', $aAttributesToGet = array("*"), $bRecursive=true) {
+    public function searchDn(string $sDn, string $sSearchFilter = '(objectclass=*)', array $aAttributesToGet = ["*"], bool $bRecursive = true): bool|array
+    {
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
-        $this->_w(__FUNCTION__ . ' DN = ' . $sDn . ' filter = ' . $sSearchFilter . ' attributes = ' . print_r($aAttributesToGet, 1).' recursive = '.($bRecursive ? 'yes' : 'no' ));
+        $this->_w(__FUNCTION__ . ' DN = ' . $sDn . ' filter = ' . $sSearchFilter . ' attributes = ' . print_r($aAttributesToGet, 1) . ' recursive = ' . ($bRecursive ? 'yes' : 'no'));
 
         $oLdapSearch = $bRecursive
-                ? ldap_search($this->_ldapConn, $sDn, $sSearchFilter, $aAttributesToGet)
-                : ldap_list  ($this->_ldapConn, $sDn, $sSearchFilter, $aAttributesToGet)
-                ;
+            ? ldap_search($this->_ldapConn, $sDn, $sSearchFilter, $aAttributesToGet)
+            : ldap_list($this->_ldapConn, $sDn, $sSearchFilter, $aAttributesToGet)
+        ;
 
         if (!$oLdapSearch) {
             $this->_w(__FUNCTION__ . " !!!ERROR!!! filter $sSearchFilter failed ");
             return false;
         }
         $aItems = ldap_get_entries($this->_ldapConn, $oLdapSearch);
-        $this->_w(__FUNCTION__ . " count of returned items: ".count($aItems));
+        $this->_w(__FUNCTION__ . " count of returned items: " . count($aItems));
         // $this->_w(__FUNCTION__ . " <pre>".print_r($aItems,1).'</pre>');
         return $aItems;
     }
@@ -371,9 +383,10 @@ class imlldap {
      * @param array   $aAttributesToGet  flat array of attributes to fetch
      * @param bool    $bRecursive        flag: recursive search? default: true (=yes, recursive)
      * 
-     * @return array
+     * @return boolean|array
      */
-    public function searchUser($sSearchFilter='', $aAttributesToGet = array("*"), $bRecursive=true) {
+    public function searchUser(string $sSearchFilter = '', array $aAttributesToGet = ["*"], bool $bRecursive = true): bool|array
+    {
         return $this->searchDn($this->_aLdap['DnUserNode'], $sSearchFilter, $aAttributesToGet, $bRecursive);
         /*
         if (!$this->_ldapBind) {
@@ -396,13 +409,14 @@ class imlldap {
      * It returns false if the user does not exist or is
      * not member of the group 'DnAppNode' (if it was set).
      * 
-     * @param string $sUser             user id (uid) or email (mail) to search
-     * @param array  $aAttributesToGet  i.e. array("ou", "sn", "vorname", "mail", "uid", "memberOf")
+     * @param string  $sUser             user id (uid) or email (mail) to search
+     * @param array   $aAttributesToGet  i.e. ["ou", "sn", "vorname", "mail", "uid", "memberOf"]
      * @return boolean|array
      */
-    public function getUserInfo($sUser, $aAttributesToGet = array("*")) {
+    public function getUserInfo(string $sUser, array $aAttributesToGet = ["*"]): bool|array
+    {
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
@@ -430,12 +444,13 @@ class imlldap {
      * email address. It returns false if the user does not exist or is
      * not member of the group 'DnAppNode' (if it was set).
      * 
-     * @param type $sUser
+     * @param string $sUser
      * @return string
      */
-    public function getUserDn($sUser) {
+    public function getUserDn(string $sUser): bool|string
+    {
         $this->_w(__FUNCTION__ . '(' . $sUser . ')');
-        $aItem = $this->getUserInfo($sUser, array("dn"));
+        $aItem = $this->getUserInfo($sUser, ["dn"]);
         if (is_array($aItem) && array_key_exists('dn', $aItem)) {
             $this->_w(__FUNCTION__ . ' OK: dn was found ' . $aItem['dn']);
             return $aItem['dn'];
@@ -452,15 +467,16 @@ class imlldap {
      * @param string  $sPW    password
      * @return boolean
      */
-    public function setPassword($sUser, $sPW) {
+    public function setPassword(string $sUser, string $sPW): bool
+    {
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
         $sDn = $this->getUserDn($sUser);
         if ($sDn) {
-            if (!ldap_mod_replace($this->_ldapConn, $sDn, array('userpassword' => "{MD5}" . base64_encode(pack("H*", md5($sPW)))))) {
+            if (!ldap_mod_replace($this->_ldapConn, $sDn, ['userpassword' => "{MD5}" . base64_encode(pack("H*", md5($sPW)))])) {
                 $this->_wLdaperror(__FUNCTION__);
                 return false;
             } else {
@@ -478,17 +494,18 @@ class imlldap {
      * @param string   $Input
      * @return string
      */
-    private function _getNTLMHash($Input) {
+    private function _getNTLMHash(string $Input): string
+    {
         // Convert the password from UTF8 to UTF16 (little endian)
         $Input = iconv('UTF-8', 'UTF-16LE', $Input);
 
         // Encrypt it with the MD4 hash
-        $MD4Hash=hash('md4',$Input);
+        $MD4Hash = hash('md4', $Input);
         // Make it uppercase, not necessary, but it's common to do so with NTLM hashes
         $NTLMHash = strtoupper($MD4Hash);
 
         // Return the result
-        return($NTLMHash);
+        return ($NTLMHash);
     }
 
     /**
@@ -502,15 +519,19 @@ class imlldap {
      * @param string  $sPW    password
      * @return boolean
      */
-    public function setPasswordSamba($sUser, $sPW) {
+    public function setPasswordSamba(string $sUser, string $sPW): bool
+    {
         $sDn = $this->getUserDn($sUser);
         if ($sDn) {
             $sPwField = 'sambaNTPassword';
             $sPwValue = $this->_getNTLMHash($sPW);
-            return $this->objUpdate($sDn, array(
-                        $sPwField => $sPwValue,
-                        'SambaPwdLastSet' => date('U'),
-            ));
+            return $this->objUpdate(
+                $sDn,
+                [
+                    $sPwField => $sPwValue,
+                    'SambaPwdLastSet' => date('U'),
+                ]
+            );
         }
         $this->_w(__FUNCTION__ . ' dn not found (user does not exist in ldap) ' . $sUser);
         return false;
@@ -519,15 +540,17 @@ class imlldap {
     /**
      * update an ldap object
      * this requires a ldap bind with master/ admin account
+     * It returns true if the action was successful
      * 
      * @param string  $sDn     dn to update
      * @param array   $aItem   array of new ldap properties
      * @return boolean
      */
-    public function objAdd($sDn, $aItem) {
-        $this->_w(__FUNCTION__ . '("' . $sDn . '", <pre>['.print_r($aItem, 1).']</pre>)');
+    public function objAdd(string $sDn, array $aItem): bool
+    {
+        $this->_w(__FUNCTION__ . '("' . $sDn . '", <pre>[' . print_r($aItem, 1) . ']</pre>)');
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
@@ -543,13 +566,14 @@ class imlldap {
      * this requires a ldap bind with master/ admin account
      * 
      * @param string  $sDn     dn to update
-     * @param string  $aItem   array of new ldap properties
+     * @param array   $aItem   array of new ldap properties
      * @return boolean
      */
-    public function objAddAttr($sDn, $aItem) {
+    public function objAddAttr(string $sDn, array $aItem): bool
+    {
         $this->_w(__FUNCTION__ . '("' . $sDn . '", [array])');
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
@@ -567,23 +591,27 @@ class imlldap {
         return false;
     }
 
-     /**
+    /**
      * read attributes from ldap node with given DN (using ldap_read)
+     * It returns "false" if the action was not successful
+     * - no ldap connection
+     * - DN or filter didn't match
      * 
      * @param string  $sDn               DN to search for
      * @param string  $sSearchFilter     filter in ldap filter syntax
      * @param array   $aAttributesToGet  flat array of attributes to fetch
-     * @return array
+     * @return boolean|array
      */
-    public function objGet($sDn, $sSearchFilter='(objectclass=*)', $aAttributesToGet = array("*")) {
+    public function objGet(string $sDn, string $sSearchFilter = '(objectclass=*)', array $aAttributesToGet = ["*"]): bool|array
+    {
 
-        $this->_w(__FUNCTION__ . '("' . $sDn . '", filter = '.$sSearchFilter.', atttr= '.print_r($aAttributesToGet, 1).' )');
+        $this->_w(__FUNCTION__ . '("' . $sDn . '", filter = ' . $sSearchFilter . ', atttr= ' . print_r($aAttributesToGet, 1) . ' )');
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
-        
+
         $oLdapResult = ldap_read($this->_ldapConn, $sDn, $sSearchFilter, $aAttributesToGet);
 
         if (!$oLdapResult) {
@@ -597,15 +625,17 @@ class imlldap {
      * update an ldap object with given key-value array
      * if the attribute (key) does not exist it will be created.
      * this requires a ldap bind with master/ admin account
+     * It returns "false" if the action failed
      * 
      * @param string  $sDn    full DN where to update the item
      * @param array   $aItem  updated entry
      * @return boolean
      */
-    public function objUpdate($sDn, $aItem) {
+    public function objUpdate(string $sDn, array $aItem): bool
+    {
         $this->_w(__FUNCTION__ . '("' . $sDn . '", ' . print_r($aItem, 1) . ')');
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
@@ -624,14 +654,16 @@ class imlldap {
     /**
      * delete an ldap object
      * this requires a ldap bind with master/ admin account
+     * It returns "false" if the action failed
      * 
      * @param string  $sDn    full DN to remove 
      * @return boolean
      */
-    public function objDelete($sDn) {
+    public function objDelete(string $sDn): bool
+    {
         $this->_w(__FUNCTION__ . '("' . $sDn . '")');
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
@@ -640,7 +672,8 @@ class imlldap {
             if (!ldap_delete($this->_ldapConn, $sDn)) {
                 $this->_wLdaperror(__FUNCTION__);
                 return false;
-            } return true;
+            }
+            return true;
         }
         $this->_w(__FUNCTION__ . ' missing parameter for DN');
         return false;
@@ -649,17 +682,21 @@ class imlldap {
     /**
      * delete attributes of an ldap object
      * this requires a ldap bind with master/ admin account
+     * It returns "false" if the action failed
      * 
-     * TODO: Test me
+     * @example:
+     * remove attribute "userPassword" of user $sUserDn:
+     * <code>$oLdap->objDeleteAttr($sUserDn, ['userPassword'=>[]]</code>
      * 
      * @param string  $sDn    DN
-     * @param string  $aItem  item to remove
+     * @param array   $aItem  item to remove
      * @return boolean
      */
-    public function objDeleteAttr($sDn, $aItem) {
+    public function objDeleteAttr(string $sDn, array $aItem): bool
+    {
         $this->_w(__FUNCTION__ . '("' . $sDn . '", [array])');
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
@@ -669,7 +706,8 @@ class imlldap {
             if (!ldap_mod_del($this->_ldapConn, $sDn, $aItem)) {
                 $this->_wLdaperror(__FUNCTION__);
                 return false;
-            } return true;
+            }
+            return true;
         }
         $this->_w(__FUNCTION__ . ' dn not found (item does not exist in ldap) or item was not an array ' . print_r($aItem, 1));
         return false;
@@ -683,15 +721,16 @@ class imlldap {
      * @param string  $sAttrValue  value to check
      * @return boolean
      */
-    public function objectAttributeExists($sDn, $sAttribute) {
+    public function objectAttributeExists(string $sDn, string $sAttribute): bool
+    {
         $this->_w(__FUNCTION__ . '("' . $sDn . '", "' . $sAttribute . '")');
 
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
-        $aData = $this->searchDn($sDn, '(&(objectclass=top))', array($sAttribute));
+        $aData = $this->searchDn($sDn, '(&(objectclass=top))', [$sAttribute]);
         $return = (is_array($aData) && isset($aData[0][strtolower($sAttribute)]));
         $this->_w(__FUNCTION__ . '(...) returns ' . ($return ? 'true' : 'false'));
         return $return;
@@ -705,15 +744,16 @@ class imlldap {
      * @param string  $sAttrValue  value to check
      * @return boolean
      */
-    public function objectAttributeAndValueExist($sDn, $sAttribute, $sAttrValue) {
+    public function objectAttributeAndValueExist(string $sDn, string $sAttribute, string $sAttrValue): bool
+    {
         $this->_w(__FUNCTION__ . '("' . $sDn . '", "' . $sAttribute . '", "' . $sAttrValue . '")');
 
         if (!$this->_ldapBind) {
-            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){
+            if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) {
                 return false;
             }
         }
-        $aData = $this->searchDn($sDn, '(&(objectclass=top))', array($sAttribute));
+        $aData = $this->searchDn($sDn, '(&(objectclass=top))', [$sAttribute]);
         $return = (is_array($aData) && isset($aData[0][strtolower($sAttribute)]) && array_search($sAttrValue, $aData[0][strtolower($sAttribute)]) !== false);
         $this->_w(__FUNCTION__ . '(...) returns ' . ($return ? 'true' : 'false'));
         return $return;
@@ -728,7 +768,8 @@ class imlldap {
      * @param string  $sAttrValue  value to check
      * @return boolean
      */
-    public function objectAttributeAndValueMustExist($sDn, $sAttribute, $sAttrValue) {
+    public function objectAttributeAndValueMustExist(string $sDn, string $sAttribute, string $sAttrValue): bool
+    {
         $this->_w(__FUNCTION__ . '("' . $sDn . '", "' . $sAttribute . '", "' . $sAttrValue . '")');
         // return if it already exists
         if ($this->objectAttributeAndValueExist($sDn, $sAttribute, $sAttrValue)) {
@@ -737,7 +778,7 @@ class imlldap {
 
         // create it
         $this->_w(__FUNCTION__ . " create $sAttribute = $sAttrValue");
-        $return = $this->objAddAttr($sDn, array($sAttribute => $sAttrValue));
+        $return = $this->objAddAttr($sDn, [$sAttribute => $sAttrValue]);
         return $return;
     }
 
@@ -749,7 +790,8 @@ class imlldap {
      * @param string  $sDn    optional DN where to create the user
      * @return boolean
      */
-    public function userAdd($aItem, $sDn = false) {
+    public function userAdd(array $aItem, string $sDn = ""): bool
+    {
         if (!$sDn) {
             $sDn = 'cn=' . $aItem['cn'] . ',' . $this->_aLdap['DnUserNode'];
         }
@@ -769,7 +811,8 @@ class imlldap {
      * @param string  $sPW    new password to set
      * @return boolean
      */
-    public function userDelete($sUserDn) {
+    public function userDelete(string $sUserDn): bool
+    {
         $this->_w(__FUNCTION__ . '(' . $sUserDn . ')');
         return $this->objDelete($sUserDn);
     }
@@ -781,7 +824,8 @@ class imlldap {
      * @param array   $aItem  new user data to update
      * @return boolean
      */
-    public function userUpdate($aItem) {
+    public function userUpdate(array $aItem): bool
+    {
         $this->_w(__FUNCTION__ . '([array])');
         $sDn = $this->getUserDn($aItem['uid']);
         if ($sDn) {
@@ -801,7 +845,8 @@ class imlldap {
      * @param string  $sPW    password
      * @return boolean
      */
-    public function verifyPassword($sUser, $sPW) {
+    public function verifyPassword(string $sUser, string $sPW): bool
+    {
         $sDn = $this->getUserDn($sUser);
         if ($sDn) {
             return $this->bind($sDn, $sPW);
diff --git a/public_html/deployment/classes/logger.class.php b/public_html/deployment/classes/logger.class.php
index a25fcc959bdfc1ea6681c56a093d02edc1583c57..3cd48d4911471734c7636507e1a71a137fd9848b 100644
--- a/public_html/deployment/classes/logger.class.php
+++ b/public_html/deployment/classes/logger.class.php
@@ -27,15 +27,18 @@
  * 2022-10-16  mark longest action with an icon 
  * 2022-12-15  make it compatible to PHP 8.2; add doc + comments
  * 2023-05-15  fix _getBar() - division by zero
+ * 2024-07-12  php8 only: use variable types; update phpdocs
+ * 2024-09-04  fix short array syntax
  * ----------------------------------------------------------------------
  */
-class logger {
+class logger
+{
 
     /**
      * @var {array} array of added messages
      */
     protected $aMessages = [];
-    
+
     /**
      * @var {bool} flag: show debug infos? default: false
      */
@@ -51,21 +54,22 @@ class logger {
      */
     protected $sCssPrefix = '';
 
+    protected $sSourceUrl = 'https://github.com/axelhahn/ahlogger';
+
     // ----------------------------------------------------------------------
     // CONSTRUCTOR
     // ----------------------------------------------------------------------
 
     /**
-     * constuctor
+     * Constuctor
      * @param  string $sInitMessage  init message
-     * @return boolean
      */
-    public function __construct($sInitMessage = "Logger was initialized.") {
-        $this->_iMemStart=memory_get_usage();
+    public function __construct(string $sInitMessage = "Logger was initialized.")
+    {
+        $this->_iMemStart = memory_get_usage();
         $this->enableDebug(true);
         $this->add($sInitMessage);
-        $this->sCssPrefix='debug-'.md5(microtime(true));
-        return true;
+        $this->sCssPrefix = 'debug-' . md5(microtime(true));
     }
 
     // ----------------------------------------------------------------------
@@ -73,51 +77,55 @@ class logger {
     // ----------------------------------------------------------------------
 
     /**
-     * add a logging message
-     * @param type $sMessage
-     * @param type $sLevel
+     * Add a logging message
+     * @param string $sMessage
+     * @param string $sLevel
      * @return boolean
      */
-    public function add($sMessage, $sLevel = "info") {
-        if (!$this->bShowDebug){
+    public function add(string $sMessage, string $sLevel = "info"): bool
+    {
+        if (!$this->bShowDebug) {
             return false;
-        }        
-        $this->aMessages[] = array(
+        }
+        $this->aMessages[] = [
             'time' => microtime(true),
             'message' => $sMessage,
             'level' => preg_replace('/[^a-z0-9\-\_]/', '', $sLevel),
             'memory' => memory_get_usage()
-        );
+        ];
 
         return true;
     }
 
     /**
-     * enable / disable debugging
-     * @param type $bEnable
-     * @return type
+     * Enable / disable debugging
+     * @param bool $bEnable
+     * @return bool
      */
-    public function enableDebug($bEnable=true){
-        return $this->bShowDebug=!!$bEnable;
+    public function enableDebug(bool $bEnable = true): bool
+    {
+        return $this->bShowDebug = !!$bEnable;
     }
 
     /**
-     * enable client debugging by a given array of allowed ip addresses
+     * Enable client debugging by a given array of allowed ip addresses
      * @param array $aIpArray list of ip addresses in a flat array
      * @return boolean
      */
-    public function enableDebugByIp($aIpArray){
+    public function enableDebugByIp(array $aIpArray): bool
+    {
         $this->enableDebug(false);
-        if (!$_SERVER || !is_array($_SERVER) || !array_key_exists("REMOTE_ADDR", $_SERVER)){
+        if (!$_SERVER || !is_array($_SERVER) || !array_key_exists("REMOTE_ADDR", $_SERVER)) {
             return false;
         }
-        if (array_search($_SERVER['REMOTE_ADDR'], $aIpArray)!==false){
+        if (array_search($_SERVER['REMOTE_ADDR'], $aIpArray) !== false) {
             $this->enableDebug(true);
         }
+        return true;
     }
 
     /**
-     * helper function: prepare array of added massages before output
+     * Helper function: prepare array of added massages before output
      * - detect warnings and errors
      * - detect needed time for each action
      * - detect longest action
@@ -126,14 +134,15 @@ class logger {
      * 
      * @return array
      */
-    protected function _prepareRendering(){
-        $iMem=memory_get_usage();
+    protected function _prepareRendering(): array
+    {
+        $iMem = memory_get_usage();
         $this->add('<hr>');
         $this->add('Memory on start: ' . number_format($this->_iMemStart, 0, '.', ',') . " bytes");
-        $this->add('Memory on end: '   . number_format($iMem, 0, '.', ',') . " bytes");
-        $this->add('Memory peak: '  . number_format(memory_get_peak_usage(), 0, '.', ',') . " bytes");
+        $this->add('Memory on end: ' . number_format($iMem, 0, '.', ',') . " bytes");
+        $this->add('Memory peak: ' . number_format(memory_get_peak_usage(), 0, '.', ',') . " bytes");
 
-        $aReturn=[
+        $aReturn = [
             'totaltime' => false,
             'level' => false,
             'warnings' => '',
@@ -154,74 +163,77 @@ class logger {
         foreach ($this->aMessages as $aLogentry) {
             $iCounter++;
 
-            if($aLogentry["level"]=="warning"){
-                $bHasWarning=true;
+            if ($aLogentry["level"] == "warning") {
+                $bHasWarning = true;
             }
-            if($aLogentry["level"]=="error"){
-                $bHasError=true;
+            if ($aLogentry["level"] == "error") {
+                $bHasError = true;
             }
 
-            $sTrId = $this->sCssPrefix.'debugTableRow' . $iCounter;
+            $sTrId = $this->sCssPrefix . 'debugTableRow' . $iCounter;
             $iDelta = $aLogentry["time"] - $iLasttime;
             if ($iDelta > $iMaxtime) {
                 $iMaxtime = $iDelta;
                 $sMaxRowId = $sTrId;
             }
-            $iMaxmem=max($aLogentry["memory"], $iMaxmem);
+            $iMaxmem = max($aLogentry["memory"], $iMaxmem);
 
 
             if (($iDelta > 1) || $aLogentry["level"] == "warning") {
-                $aReturn['warnings'].='<a href="#' . $sTrId . '" title="' . sprintf("%01.4f", $iDelta) . ' s">' . $iCounter . '</a>&nbsp;';
+                $aReturn['warnings'] .= '<a href="#' . $sTrId . '" title="' . sprintf("%01.4f", $iDelta) . ' s">' . $iCounter . '</a>&nbsp;';
             }
             if ($aLogentry["level"] == "error") {
-                $aReturn['errors'].='<a href="#' . $sTrId . '" title="' . sprintf("%01.4f", $iDelta) . ' s">' . $iCounter . '</a>&nbsp;';
+                $aReturn['errors'] .= '<a href="#' . $sTrId . '" title="' . sprintf("%01.4f", $iDelta) . ' s">' . $iCounter . '</a>&nbsp;';
             }
-            $aReturn['entries'][]=[
-                'time'=>$aLogentry["time"],
-                'level'=>$aLogentry["level"],
-                'message'=>$aLogentry["message"],
-                'memory'=>sprintf("%01.2f", $aLogentry["memory"]/1024/1024), // MB
+            $aReturn['entries'][] = [
+                'time' => $aLogentry["time"],
+                'level' => $aLogentry["level"],
+                'message' => $aLogentry["message"],
+                'memory' => sprintf("%01.2f", $aLogentry["memory"] / 1024 / 1024), // MB
 
-                'trid'=>$sTrId,
-                'trclass'=>$aLogentry["level"],
-                'counter'=>$iCounter,
-                'timer'=>sprintf("%01.3f", $aLogentry["time"] - $sStarttime),
-                'delta'=>sprintf("%01.0f", $iDelta*1000),
+                'trid' => $sTrId,
+                'trclass' => $aLogentry["level"],
+                'counter' => $iCounter,
+                'timer' => sprintf("%01.3f", $aLogentry["time"] - $sStarttime),
+                'delta' => sprintf("%01.0f", $iDelta * 1000),
             ];
             $iLasttime = $aLogentry["time"];
         }
-        $aReturn['level']=($bHasWarning
+        $aReturn['level'] = ($bHasWarning
             ? ($bHasError ? 'error' : 'warning')
             : ''
         );
-        $aReturn['maxrowid']=$sMaxRowId;
-        $aReturn['maxtime']=sprintf("%01.3f", $iMaxtime);
-        $aReturn['maxmem']=sprintf("%01.2f", $iMaxmem/1024/1024);
-        $aReturn['totaltime']=sprintf("%01.3f", $aLogentry['time']-$aReturn['entries'][0]['time']);
+        $aReturn['maxrowid'] = $sMaxRowId;
+        $aReturn['maxtime'] = sprintf("%01.3f", $iMaxtime);
+        $aReturn['maxmem'] = sprintf("%01.2f", $iMaxmem / 1024 / 1024);
+        $aReturn['totaltime'] = sprintf("%01.3f", $aLogentry['time'] - $aReturn['entries'][0]['time']);
         return $aReturn;
     }
 
     /**
-     * get html code for a progressbar with divs
-     * @param  {int|float}  $iVal  value between 0..max value
-     * @param  {int|float}  $iMax  max value
-     * @return {string}
+     * Get html code for a progressbar with divs
+     * @param  int|float  $iVal  value between 0..max value
+     * @param  int|float  $iMax  max value
+     * @return string
      */
-    protected function _getBar($iVal, $iMax){
-        return $iMax>0
-            ? '<div class="bar"><div class="progress" style="width: '.($iVal/$iMax*100).'%;">&nbsp;</div></div>'
+    protected function _getBar(int|float $iVal, int|float $iMax): string
+    {
+        return $iMax > 0
+            ? '<div class="bar"><div class="progress" style="width: ' . ($iVal / $iMax * 100) . '%;">&nbsp;</div></div>'
             : ''
-            ;
+        ;
     }
 
     /**
-     * render output of all logging messages
+     * Render output of all logging messages
+     * @return string
      */
-    public function render() {
-        if (!$this->bShowDebug){
+    public function render(): string
+    {
+        if (!$this->bShowDebug) {
             return false;
         }
-        $aData=$this->_prepareRendering();
+        $aData = $this->_prepareRendering();
 
         /*
         Array
@@ -252,57 +264,58 @@ class logger {
                     )
         */
 
-        $sOut='';
+        $sOut = '';
         // echo '<pre>'; print_r($aData); die();
-        foreach ($aData['entries'] as $aLogentry){
-            $sOut.='<tr class="'.$this->sCssPrefix.'-level-' . $aLogentry["level"] . ''.($aLogentry["trid"]==$aData["maxrowid"] ? ' '.$this->sCssPrefix.'-maxrow' : '').'" '
-                .'id="' . $aLogentry["trid"] . '">' .
-                    '<td>' . $aLogentry["counter"] . '</td>' .
-                    '<td>' . $aLogentry["level"] . '</td>' .
-                    '<td>' . $aLogentry["timer"] . '</td>' .
-                    '<td>' . $this->_getBar($aLogentry["delta"], $aData["maxtime"]*1000). $aLogentry["delta"] .' ms'.($aLogentry["delta"]==$aData['maxtime']*1000 ? ' ⏱️' : '').'</td>' .
-                    '<td>' . $this->_getBar($aLogentry["memory"], $aData["maxmem"]) . $aLogentry["memory"] .' MB'. '</td>' .
-                    '<td>' . $aLogentry["message"] . '</td>' .
-                    '</tr>';
+        foreach ($aData['entries'] as $aLogentry) {
+            $sOut .= '<tr class="' . $this->sCssPrefix . '-level-' . $aLogentry["level"] . '' . ($aLogentry["trid"] == $aData["maxrowid"] ? ' ' . $this->sCssPrefix . '-maxrow' : '') . '" '
+                . 'id="' . $aLogentry["trid"] . '">' .
+                '<td align="right">' . $aLogentry["counter"] . '</td>' .
+                '<td>' . $aLogentry["level"] . '</td>' .
+                '<td align="right">' . $aLogentry["timer"] . '</td>' .
+                '<td align="right">' . $this->_getBar($aLogentry["delta"], $aData["maxtime"] * 1000) . ($aLogentry["delta"] == $aData['maxtime'] * 1000 ? '⏱️    ' : '') . $aLogentry["delta"] . ' ms</td>' .
+                '<td align="right">' . $this->_getBar($aLogentry["memory"], $aData["maxmem"]) . $aLogentry["memory"] . ' MB' . '</td>' .
+                '<td>' . $aLogentry["message"] . '</td>' .
+                '</tr>';
         }
-        if ($sOut){
+        if ($sOut) {
             $sOut = '
             <style>
-                .'.$this->sCssPrefix.'-info          {position: fixed; top: 6em; right: 1em; background: rgba(200,228,255, 0.8); border: 1px solid; z-index: 99999;}
-                .'.$this->sCssPrefix.'-info .head    {background: rgba(0,0,0,0.4); color: #fff;padding: 0em 0.5em 0.2em; }
-                .'.$this->sCssPrefix.'-info .content {padding: 0.5em; }
-                .'.$this->sCssPrefix.'-info .content .total {font-size: 160%; color: rgba(0,0,0,0.5); margin: 0.3em 0; display: inline-block;}
+                .' . $this->sCssPrefix . '-info {position: fixed; top: 6em; right: 1em; background: rgba(230,240,255, 0.8); border: 2px solid rgba(0,0,0,0.2); border-radius: 0.3em; z-index: 99999;}
+                .' . $this->sCssPrefix . '-info .loggerhead    {background: rgba(0,0,0,0.4); color: #fff;padding: 0em 0.5em 0.2em; border-radius: 0.3em 0.3em 0 0; }
+                .' . $this->sCssPrefix . '-info .loggercontent {padding: 0.5em; }
+                .' . $this->sCssPrefix . '-info .loggercontent .total {font-size: 160%; color: rgba(0,0,0,0.5); margin: 0.3em 0; display: inline-block;}
 
-                .'.$this->sCssPrefix.'-messages {margin: 5em 2em 2em;}
-                .'.$this->sCssPrefix.'-messages .bar      {background: rgba(0,0,0,0.03); height: 1.4em; position: absolute; width: 6em; border-right: 1px solid rgba(0,0,0,0.2);}
-                .'.$this->sCssPrefix.'-messages .progress {background: rgba(100,140,180,0.2); height: 1.4em; padding: 0;}
-                .'.$this->sCssPrefix.'-messages table{background: #fff; color: #222;table-layout:fixed; }
-                .'.$this->sCssPrefix.'-messages table th{background: none;}
-                .'.$this->sCssPrefix.'-messages table th.barcol{min-width: 7em; position: relative;}
-                .'.$this->sCssPrefix.'-messages table td{padding: 3px; vertical-align: top;}
-                .'.$this->sCssPrefix.'-messages table th:hover{background:#aaa !important;}
+                .' . $this->sCssPrefix . '-messages {margin: 5em 2em 2em;}
+                .' . $this->sCssPrefix . '-messages>h3 {font-size: 150%; margin: 0 0 0.5em 0;}
+                .' . $this->sCssPrefix . '-messages .bar      {background: rgba(0,0,0,0.03); height: 1.4em; position: absolute; width: 6em; border-right: 1px solid rgba(0,0,0,0.2);}
+                .' . $this->sCssPrefix . '-messages .progress {background: rgba(100,140,180,0.2); height: 1.4em; padding: 0; float: left;}
+                .' . $this->sCssPrefix . '-messages table{background: #fff; color: #222;table-layout:fixed; border: 2px solid rgba(0,0,0,0.2); border-radius: 0.5em;}
+                .' . $this->sCssPrefix . '-messages table th{background: none; color: #222; border-bottom: 2px solid rgba(0,0,0,0.4);}
+                .' . $this->sCssPrefix . '-messages table th.barcol{min-width: 7em; position: relative;}
+                .' . $this->sCssPrefix . '-messages table td{padding: 3px; vertical-align: top;}
+                .' . $this->sCssPrefix . '-messages table th:hover{background:#aaa !important;}
 
-                .'.$this->sCssPrefix.'-level-info{background: #e0e8f8; color:#124}
-                .'.$this->sCssPrefix.'-level-warning{background: #fcf8e3; color: #980;}
-                .'.$this->sCssPrefix.'-level-error{background: #fce0e0; color: #944;}
-                .'.$this->sCssPrefix.'-maxrow{color:#f33; font-weight: bold;}
+                .' . $this->sCssPrefix . '-level-info{background: #f0f4f4; color:#124}
+                .' . $this->sCssPrefix . '-level-warning{background: #fcf8e3; color: #980;}
+                .' . $this->sCssPrefix . '-level-error{background: #fce0e0; color: #944;}
+                .' . $this->sCssPrefix . '-maxrow{color:#f33; font-weight: bold;}
             </style>
-            <div class="'.$this->sCssPrefix.' '.$this->sCssPrefix.'-info '.$this->sCssPrefix.'-level-'.$aData['level'].'">
-                <div class="head">ahLogger</div>
-                <div class="content">
+            <div class="' . $this->sCssPrefix . ' ' . $this->sCssPrefix . '-info ' . $this->sCssPrefix . '-level-' . $aData['level'] . '" onclick="location.href=\'#' . $this->sCssPrefix . '-messages\';">
+                <div class="loggerhead">ahLogger</div>
+                <div class="loggercontent">
                     <span class="total">⏱️ ' . $aData['totaltime'] . '&nbsp;s</span><br>
-                    🪲 <a href="#'.$this->sCssPrefix.'-messages">Debug infos</a> | 🔺 <a href="#">top</a><br>
-                    <span>longest&nbsp;action: ⏱️&nbsp;<a href="#' . $aData['maxrowid'] . '">' . ($aData['maxtime']*1000) . '&nbsp;ms</a></span>
-                    ' . ($aData['errors'] ? '<br><span>‼️ Errors: '.$aData['errors'] . '</span>' : '').'
-                    ' . ($aData['warnings'] ? '<br><span>⚠️ Warnings: '.$aData['warnings'] . '</span>' : '').'
+                    🪲 <a href="#' . $this->sCssPrefix . '-messages">Debug infos</a> | 🔺 <a href="#">top</a><br>
+                    <span>longest&nbsp;action: ⏱️&nbsp;<a href="#' . $aData['maxrowid'] . '">' . ($aData['maxtime'] * 1000) . '&nbsp;ms</a></span>
+                    ' . ($aData['errors'] ? '<br><span>‼️ Errors: ' . $aData['errors'] . '</span>' : '') . '
+                    ' . ($aData['warnings'] ? '<br><span>⚠️ Warnings: ' . $aData['warnings'] . '</span>' : '') . '
                 </div>
             </div>
 
-            <div id="'.$this->sCssPrefix.'-messages" class="'.$this->sCssPrefix.' '.$this->sCssPrefix.'-messages">
-            DEBUG :: LOG MESSAGES<br>'
-            . ($aData['errors']   ? '<span>Errors: '.$aData['errors'] . '</span><br>' : '')
-            . ($aData['warnings'] ? '<span>Warnings: '.$aData['warnings'] . '</span><br>' : '')
-            .'<br>
+            <div id="' . $this->sCssPrefix . '-messages" class="' . $this->sCssPrefix . ' ' . $this->sCssPrefix . '-messages">
+            <h3>ahLogger 🪳 Debug messages</h3>'
+                . ($aData['errors'] ? '<span>Errors: ' . $aData['errors'] . '</span><br>' : '')
+                . ($aData['warnings'] ? '<span>Warnings: ' . $aData['warnings'] . '</span><br>' : '')
+                . '<br>
             <table >
             <thead>
             <tr>
@@ -313,32 +326,36 @@ class logger {
                 <th class="barcol">memory</th>
                 <th>message</th>
             </tr></thead><tbody>
-            ' . $sOut . '</tbody></table>'
+            ' . $sOut 
+            . '</tbody></table>'
+            . '🌐 <a href="'.$this->sSourceUrl.'" target="_blank">'.$this->sSourceUrl.'</a>'
             ;
-		}
+        }
         return $sOut;
     }
-   /**
-     * render output of all logging messages for cli output
+
+    /**
+     * Render output of all logging messages for cli output
      * @return string
      */
-    public function renderCli(){
-        if (!$this->bShowDebug){
+    public function renderCli(): string
+    {
+        if (!$this->bShowDebug) {
             return false;
         }
-        $aData=$this->_prepareRendering();
+        $aData = $this->_prepareRendering();
 
-        $sOut='';
-        foreach ($aData['entries'] as $aLogentry){
-            $sOut.=$aLogentry["timer"].' | '
-                    .$aLogentry["delta"].' ms | '
-                    .$aLogentry["level"].' | '
-                    .(sprintf("%01.3f", $aLogentry["memory"]/1024/1024)).' MB | '
-                   .$aLogentry["message"].' '
-                   . "\n"
-                   ;
+        $sOut = '';
+        foreach ($aData['entries'] as $aLogentry) {
+            $sOut .= $aLogentry["timer"] . ' | '
+                . $aLogentry["delta"] . ' ms | '
+                . $aLogentry["level"] . ' | '
+                . (sprintf("%01.3f", $aLogentry["memory"] / 1024 / 1024)) . ' MB | '
+                . $aLogentry["message"] . ' '
+                . "\n"
+            ;
         }
-        $sOut.="\nTotal time: ".$aData['totaltime'] . "\n";
+        $sOut .= "\nTotal time: " . $aData['totaltime'] . "\n";
         return $sOut;
     }
 }
diff --git a/public_html/deployment/classes/messenger.class.php b/public_html/deployment/classes/messenger.class.php
index f888cefbadd64ea35d841a27788db0d6f4f80525..3e8caac7378a1b6856a3be6bb5450dad0486847f 100644
--- a/public_html/deployment/classes/messenger.class.php
+++ b/public_html/deployment/classes/messenger.class.php
@@ -4,70 +4,80 @@
  * send messenger notifications
  *
  * @author hahn
+ * 
+ * 2024-08-23  v1.1  Axel Hahn  php8 only; added variable types
  */
-class messenger {
+class messenger
+{
 
     /**
      * config array for messengers
-     * @var type 
+     * @var array 
      */
-    protected $_aCfg = array();
-    
+    protected array $_aCfg = [];
+
     /**
      * content of messagetext to send
      * @var string
      */
-    protected $_sMessage = '';
+    protected string $_sMessage = '';
 
     /**
+     * Constructor
      * 
      * @example
-     * $oMessenger = new messenger(array(
-     *   'slack'=>array(
+     * $oMessenger = new messenger([
+     *   'slack'=>[
      *     'incomingurl'=>[WebHook url],
      *     'user'=>[visible username in slack],
      *     'icon'=>[Slack Icon], // hm, does not seem to work
-     *   ),
-     *   'email'=>array(
+     *   ],
+     *   'email'=>[
      *     'from'=>[senders e-mail]
      *     'to'=>[email-address(es)] // multiple emails must be delimited with ";"
-     *   )
-     * ));
+     *   ]
+     * ]);
      * @param array $aCfg  config array for notification targets
-     * @return boolean
      */
-    public function __construct($aCfg) {
+    public function __construct(array $aCfg)
+    {
         $this->_aCfg = $aCfg;
-        return true;
     }
 
     /**
-     * send an email if _aCfg['email']['to'] exists
+     * Send an email if _aCfg['email']['to'] exists
+     * @return bool
      */
-    private function _sendEmail(){
+    private function _sendEmail(): bool
+    {
         if (isset($this->_aCfg['email']['to']) && $this->_aCfg['email']['to']) {
             preg_match('/^(.*)\n/', $this->_sMessage, $aTmp);
             $sSubject = $aTmp[0];
             return mail(
-                $this->_aCfg['email']['to'], 
-                $sSubject, $this->_sMessage, 
+                $this->_aCfg['email']['to'],
+                $sSubject,
+                $this->_sMessage,
                 "From: " . $this->_aCfg['email']['from'] . "\r\n" .
                 "Reply-To: " . $this->_aCfg['email']['from'] . "\r\n"
             );
         }
         return false;
     }
-    
+
     /**
-     * send a message to slack if _aCfg['slack']['incomingurl'] exists
+     * Send a message to slack if _aCfg['slack']['incomingurl'] exists
+     * @return bool|string
      */
-    private function _sendToSlack(){
+    private function _sendToSlack(): bool|string
+    {
         if (isset($this->_aCfg['slack']['incomingurl']) && $this->_aCfg['slack']['incomingurl']) {
             require_once(__DIR__ . '/../../vendor/shooker/shooker.php');
             $shkr = new Shooker();
             $shkr->setupIncoming($this->_aCfg['slack']['incomingurl']);
-            $sUser=(isset($this->_aCfg['slack']['user'])? $this->_aCfg['slack']['user']: false);
-            $sIcon=(isset($this->_aCfg['slack']['icon'])? $this->_aCfg['slack']['icon']: false);
+            $sUser = (isset($this->_aCfg['slack']['user']) ? $this->_aCfg['slack']['user'] : false);
+            $sIcon = (isset($this->_aCfg['slack']['icon']) ? $this->_aCfg['slack']['icon'] : false);
+
+            // returns bool or string with response from Slack API
             return $shkr->sendMessage($this->_sMessage, $sUser, $sIcon);
         }
         return false;
@@ -77,13 +87,14 @@ class messenger {
     /**
      * send a message to all targets
      * @param string $sMessage
+     * @return void
      */
-    public function sendMessage($sMessage) {
-        $this->_sMessage=$sMessage;
+    public function sendMessage(string $sMessage): void
+    {
+        $this->_sMessage = $sMessage;
         // echo '<pre>'.print_r($this->_aCfg, 1).'</pre>'.$sMessage.'<br>';
         $this->_sendEmail();
         $this->_sendToSlack();
-        
     }
 
 }
diff --git a/public_html/deployment/classes/page.class.php b/public_html/deployment/classes/page.class.php
index 18e750c124900b2adefccd6f8018f19157768524..533d8c6b53544a35579f0bb50dc5a6874c4eac5a 100644
--- a/public_html/deployment/classes/page.class.php
+++ b/public_html/deployment/classes/page.class.php
@@ -1,52 +1,55 @@
 <?php
 
 /**
- * PIMPED APACHE-STATUS
  * Page class
  * Render output page by replacing placeholders 
  *
- * @package pimped_apache_status
+ * origin is from PIMPED APACHE-STATUS
+ * 
  * @author Axel Hahn
+ * 
+ * 2024-08-23  v1.1  Axel Hahn  php8 only; added variable types; short array syntax
  */
-class Page {
+class Page
+{
 
     /**
-     * output type of content
-     * @var array
+     * Output type of content
+     * @var string
      */
-    private $sType = 'html';
+    private string $sType = 'html';
 
     /**
-     * array of strings for http response header
+     * Array of strings for http response header
      * @var array
      */
-    private $aResponseHeader = array();
+    private array $aResponseHeader = [];
 
     /**
      * Replacements in the template
      * @var array
      */
-    private $aReplace = array(
+    private array $aReplace = [
         '{{HEADER}}' => '',
         '{{CONTENT}}' => '',
         '{{FOOTER}}' => '',
         '{{JSONREADY}}' => '',
-    );
+    ];
 
     /**
-     * constructor (it does nothing)
-     * @return boolean (true)
+     * Constructor
      */
-    public function __construct() {
+    public function __construct()
+    {
         $this->setOutputtype();
-        return true;
     }
 
     /**
-     * wrap on document ready instructions in jQuery style
-     * @return type 
+     * Wrap on document ready instructions in jQuery style
+     * @return string
      */
-    private function _finalizeJsOnReady() {
+    private function _finalizeJsOnReady(): string
+    {
         return $this->aReplace["{{JSONREADY}}"] = '
             <script>
                 $(document).ready(function() {
@@ -60,43 +63,48 @@ class Page {
     // ----------------------------------------------------------------------
 
     /**
-     * get current page content
-     * @return string
+     * Get current page content
+     * @return string HTML code
      */
-    public function getContent() {
+    public function getContent(): string
+    {
         return $this->aReplace['{{CONTENT}}'];
     }
-    
+
     /**
-     * get current footer
-     * @return type
+     * Get current footer
+     * @return string
      */
-    public function getFooter() {
+    public function getFooter(): string
+    {
         return $this->aReplace['{{FOOTER}}'];
     }
 
     /**
-     * get current header in response body
-     * @return type
+     * Get current header in response body
+     * @return string
      */
-    public function getHeader() {
+    public function getHeader(): string
+    {
         return $this->aReplace['{{HEADER}}'];
     }
 
     /**
-     * get on ready javascript instructions
-     * @return type¨
+     * Get on ready javascript instructions
+     * @return string
      */
-    public function getJsOnReady() {
+    public function getJsOnReady(): string
+    {
         return $this->aReplace['{{JSONREADY}}'];
     }
-        
-    
+
+
     /**
-     * get output type
+     * Get output type
      * @return string
      */
-    public function getOutputtype() {
+    public function getOutputtype()
+    {
         return $this->sType;
     }
 
@@ -105,12 +113,14 @@ class Page {
     // ----------------------------------------------------------------------
 
     /**
-     * add javascript for on ready execution
+     * Add javascript for on ready execution
      * @param string $s  javascript code
-     * @return boolean
+     * @return bool
      */
-    public function addJsOnReady($s) {
-        return $this->aReplace['{{JSONREADY}}'] .= $s;
+    public function addJsOnReady(string $s): string
+    {
+        $this->aReplace['{{JSONREADY}}'] .= $s;
+        return true;
     }
 
     /**
@@ -118,67 +128,78 @@ class Page {
      * @param string $s
      * @return boolean
      */
-    public function addResponseHeader($s) {
-        return $this->aResponseHeader[] = $s;
+    public function addResponseHeader(string $s)
+    {
+        $this->aResponseHeader[] = $s;
+        return true;
     }
 
     /**
-     * set html body; it replaces old content
+     * Set html body; it replaces old content
      * @param string $s  html code
      * @return boolean
      */
-    public function setContent($s) {
-        return $this->aReplace['{{CONTENT}}'] = $s;
+    public function setContent(string $s): bool
+    {
+        $this->aReplace['{{CONTENT}}'] = $s;
+        return true;
     }
     /**
-     * set footer in html body; it replaces old content
+     * Set footer in html body; it replaces old content
      * @param string $s  html code
      * @return boolean
      */
-    public function setFooter($s) {
-        return $this->aReplace['{{FOOTER}}'] = $s;
+    public function setFooter(string $s): bool
+    {
+        $this->aReplace['{{FOOTER}}'] = $s;
+        return true;
     }
 
     /**
-     * set html header; it replaces old content
+     * Set html header; it replaces old content
      * @param string $s  html code
      * @return boolean
      */
-    public function setHeader($s) {
-        return $this->aReplace['{{HEADER}}'] = $s;
+    public function setHeader(string $s): bool
+    {
+        $this->aReplace['{{HEADER}}'] = $s;
+        return true;
     }
 
     /**
-     * set javascript code on ready; it replaces old content
+     * Set javascript code on ready; it replaces old content
      * @param string $s  javascript code
      * @return boolean
      */
-    public function setJsOnReady($s) {
-        return $this->aReplace['{{JSONREADY}}'] = $s;
+    public function setJsOnReady(string $s): bool
+    {
+        $this->aReplace['{{JSONREADY}}'] = $s;
+        return true;
     }
-    
+
     /**
-     * set output type of response
+     * Set output type of response
      * @param string $sOutputType
      * @return boolean
      */
-    public function setOutputtype($sOutputType = 'html') {
-        return $this->sType = $sOutputType;
+    public function setOutputtype(string $sOutputType = 'html'): bool
+    {
+        $this->sType = $sOutputType;
+        return true;
     }
 
-
-
     // ----------------------------------------------------------------------
     // OUTPUT
     // ----------------------------------------------------------------------
 
     /**
-     * send http reponse headers and built the response body
-     * @return type
+     * Send http reponse headers and built the response body
+     * @return string
      */
-    public function render() {
-        $aS = array(); // search
-        $aR = array(); // replace
+    public function render(): string
+    {
+        $aS = []; // search
+        $aR = []; // replace
 
         $this->_finalizeJsOnReady();
 
@@ -190,12 +211,12 @@ class Page {
         $sTemplate = false;
         $sTplFile = dirname(__FILE__) . "/" . $this->sType . ".tpl.php";
         if (!file_exists($sTplFile)) {
-            die("ERROR: template for type " . $this->sType . " was not found: $sTplFile");
+            throw new Exception("ERROR: template for type " . $this->sType . " was not found: $sTplFile");
         }
 
         $sTemplate = file_get_contents($sTplFile);
         if (!$sTemplate) {
-            die("ERROR: template file $sTplFile is empty or could not be read.");
+            throw new Exception("ERROR: template file $sTplFile is empty or could not be read.");
         }
 
         foreach ($this->aResponseHeader as $sHeader) {
@@ -205,5 +226,3 @@ class Page {
     }
 
 }
-
-?>
diff --git a/public_html/deployment/classes/plugins.class.php b/public_html/deployment/classes/plugins.class.php
index 9a348df6f55420aae619a8be2a5035b398a49c9a..43769dc579df9f9e739ba4c7d8c7feb3ebf51380 100644
--- a/public_html/deployment/classes/plugins.class.php
+++ b/public_html/deployment/classes/plugins.class.php
@@ -17,91 +17,94 @@
  *
  * 
  * @author axel
+ * 
+ * 2024-08-26  v1.1  Axel Hahn  php8 only; added variable types
  */
-class ciplugins {
-    
+class ciplugins
+{
+
     /**
      * start path of all plugin types (as subdirs)
      * @var string
      */
-    protected $_sPlugindir=false;
+    protected string $_sPlugindir = '';
 
     /**
      * path of the currently set plugin
      * @var string
      */
-    protected $_sSelfdir=false;
+    protected string $_sSelfdir = '';
 
     /**
      * url of set plugin
      * @var string
      */
-    protected $_sSelfurl=false;
+    protected string $_sSelfurl = '';
 
     /**
      * current plugin type - can be set via setType or setPlugin
      * @var string
      */
-    protected $_sType=false;
+    protected string $_sType = '';
 
     /**
      * current plugin name - can be set via setPlugin
      * @var string
      */
-    protected $_sPluginname=false;
+    protected string $_sPluginname = '';
 
     /**
      * plugin language
      * @var string
      */
-    protected $_sLang = "en-en";
+    protected string $_sLang = "en-en";
 
     /**
      * plugin language texts (lang*.json)
      * @var array
      */
-    protected $_aLang = [];
+    protected array $_aLang = [];
 
     /**
      * plugin configuration data (config.json)
      * @var array
      */
-    protected $_aConfig = [];
+    protected array $_aConfig = [];
 
     /**
      * global plugins config
      * see config/config_custom.php - key plugins
      * @var array
      */
-    protected $_aGlobals = [];
+    protected array $_aGlobals = [];
 
     // ---------------------------------------------------------------
     // CONSTRUCTOR
     // ---------------------------------------------------------------
 
     /**
+     * Constructor
      * initialize plugins
      *
      * @param  array  $aGlobals  global settings for plugins
      * @return boolean
      */
-    public function __construct($aGlobals=[]) {
-
-        $this->_sPlugindir=dirname(__DIR__).'/plugins';
+    public function __construct(array $aGlobals = [])
+    {
+        $this->_sPlugindir = dirname(__DIR__) . '/plugins';
         $this->setGlobalCustoms($aGlobals);
-
-        return true;
     }
 
     /**
-     * global configs
+     * Global configs
      * see config/config_custom.php - key plugins
      * 
      * @param  array  $aGlobals  global settings for plugins
      * @return boolean
      */
-    public function setGlobalCustoms($aGlobals){
-        $this->_aGlobals=$aGlobals;
+    public function setGlobalCustoms(array $aGlobals): bool
+    {
+        $this->_aGlobals = $aGlobals;
         return true;
     }
     // ---------------------------------------------------------------
@@ -109,61 +112,65 @@ class ciplugins {
     // ---------------------------------------------------------------
 
     /**
-     * get an array of available plugin types read from filesystem
+     * Get an array of available plugin types read from filesystem
      * @return array
      */
-    public function getPluginTypes(){
-        $aReturn=[];
-        foreach(glob($this->_sPlugindir.'/*', GLOB_ONLYDIR) as $sMydir){
-            $aReturn[]=basename($sMydir);
+    public function getPluginTypes(): array
+    {
+        $aReturn = [];
+        foreach (glob($this->_sPlugindir . '/*', GLOB_ONLYDIR) as $sMydir) {
+            $aReturn[] = basename($sMydir);
         }
         return $aReturn;
     }
 
     /**
-     * get an array of available plugins read from filesystem
+     * Get an array of available plugins read from filesystem
      * 
      * @param  string  $sType  set a new type of plugin; default: use current type
      * @return array
      */
-    public function getPlugins($sType=false){
-        $aReturn=[];
-        if($sType){
-            if (!$this->setType($sType)){
+    public function getPlugins(string $sType = ''): array
+    {
+        $aReturn = [];
+        if ($sType) {
+            if (!$this->setType($sType)) {
                 return $aReturn;
             }
         }
-        foreach(glob($this->_sPlugindir.'/'.$this->_sType.'/*', GLOB_ONLYDIR) as $sMydir){
-            $aReturn[]=basename($sMydir);
+        foreach (glob($this->_sPlugindir . '/' . $this->_sType . '/*', GLOB_ONLYDIR) as $sMydir) {
+            $aReturn[] = basename($sMydir);
         }
         return $aReturn;
     }
 
     /**
-     * get an array of enabled plugins
+     * Get an array of enabled plugins
      * config/config_custom.php - key "plugins" -> [type] -> [plugin] -> enabled
      * and it must be physically available
      * 
      * @param  string  $sType  set a new type of plugin; default: use current type
      * @return array
      */
-    public function getEnabledPlugins($sType=false){
+    public function getEnabledPlugins(string $sType = ''): array
+    {
         $aReturn = [];
-        if($sType){
-            if (!$this->setType($sType)){
+        if ($sType) {
+            if (!$this->setType($sType)) {
                 return $aReturn;
             }
         }
-        if (isset($this->_aGlobals[$this->_sType]) 
+        if (
+            isset($this->_aGlobals[$this->_sType])
             && is_array($this->_aGlobals[$this->_sType])
-        ){
-            foreach($this->_aGlobals[$this->_sType] as $sPluginName=>$aData){
+        ) {
+            foreach ($this->_aGlobals[$this->_sType] as $sPluginName => $aData) {
                 if (
-                    isset($aData['enabled']) 
+                    isset($aData['enabled'])
                     && $aData['enabled']
-                    && is_dir($this->_sPlugindir.'/'.$this->_sType.'/'.$sPluginName)
-                ){
-                    $aReturn[]=$sPluginName;
+                    && is_dir($this->_sPlugindir . '/' . $this->_sType . '/' . $sPluginName)
+                ) {
+                    $aReturn[] = $sPluginName;
                 }
             }
         }
@@ -181,73 +188,80 @@ class ciplugins {
     // ---------------------------------------------------------------
 
     /**
-     * set a type for plugins ... what is a name of a subdir in the plugins directory
-     * @param  {string}  $sType  Name of a plugin type, e.g. build|rollout
+     * Set a type for plugins ... what is a name of a subdir in the plugins directory
+     * @param  string  $sType  Name of a plugin type, e.g. build|rollout
+     * @return bool
      */
-    public function setType($sType){
-        $this->_sType=false;
-        if(!$sType || !is_dir($this->_sPlugindir.'/'.$sType)){
+    public function setType(string $sType): bool
+    {
+        $this->_sType = '';
+        if (!$sType || !is_dir($this->_sPlugindir . '/' . $sType)) {
             return false;
         }
-        return $this->_sType=$sType;
+        $this->_sType = $sType;
+        return true;
     }
 
     /**
-     * reset vars before setting a new plugin;
+     * Reset vars before setting a new plugin;
      * called in testPlugin()
      * @return boolean
      */
-    protected function _resetPluginData(){
-        $this->_sPluginname=false;
-        $this->_sSelfdir=false;
-        $this->_sSelfurl=false;
-        $this->_aLang=[];
-        $this->_aConfig=[];
+    protected function _resetPluginData(): bool
+    {
+        $this->_sPluginname = false;
+        $this->_sSelfdir = false;
+        $this->_sSelfurl = false;
+        $this->_aLang = [];
+        $this->_aConfig = [];
         return true;
     }
 
     /**
-     * set a plugin without autoload of its php class
+     * Test if a plugin of given type exists
+     * 
      * It returns the path of php class for true 
      * or boolean false if it does not exist
      * 
      * This can be used standalone to embed html code 
      * without loading any php code of the plugin class.
      * 
-     * @param  {string}  $sPluginName  name of the plugin
-     * @param  {string}  $sType        optional: set a type
+     * @param  string  $sPluginName  name of the plugin
+     * @param  string  $sType        optional: set a type
      * @return bool|string
      */
-    public function testPlugin($sPluginName,$sType=false){
+    public function testPlugin(string $sPluginName, string $sType = ''): bool|string
+    {
         $this->_resetPluginData();
-        if($sType){
-            if (!$this->setType($sType)){
+        if ($sType) {
+            if (!$this->setType($sType)) {
                 return false;
             }
         }
-        $this->_sSelfdir=$this->_sPlugindir.'/'.$this->_sType.'/'.$sPluginName;
-        $sFile=$this->_sSelfdir.'/plugin.php';
-        if(!file_exists($sFile)){
+        $this->_sSelfdir = $this->_sPlugindir . '/' . $this->_sType . '/' . $sPluginName;
+        $sFile = $this->_sSelfdir . '/plugin.php';
+        if (!file_exists($sFile)) {
             // die(' MISS '.$sFile);
-            $this->_sSelfdir=false;
+            $this->_sSelfdir = false;
             return false;
         }
-        $this->_sPluginname=$sPluginName;
-        $this->_sSelfurl='/deployment/plugins/'.$this->_sType.'/'.$sPluginName;
+        $this->_sPluginname = $sPluginName;
+        $this->_sSelfurl = '/deployment/plugins/' . $this->_sType . '/' . $sPluginName;
         return $sFile;
     }
 
     /**
-     * set a plugin with autoload of its php class
+     * Set a plugin with autoload of its php class
      * It returns a boolean
      * 
-     * @param  {string}  $sPluginName  name of the plugin
-     * @param  {string}  $sType        optional: set a type
+     * @param  string  $sPluginName  name of the plugin
+     * @param  string  $sType        optional: set a type
      * @return bool
      */
-    public function setPlugin($sPluginName,$sType=false){
-        $sFile=$this->testPlugin($sPluginName,$sType);
-        if(!$sFile){
+    public function setPlugin(string $sPluginName, string $sType = ''): bool
+    {
+        $sFile = $this->testPlugin($sPluginName, $sType);
+        if (!$sFile) {
             return false;
         }
         include_once $sFile;
@@ -257,25 +271,28 @@ class ciplugins {
     // getter for plugin
     // ---------------------------------------------------------------
     /**
-     * get config entry
+     * Get config entry of a plugin
      * @param  string  $sKey  name of config value
+     * @return mixed
      */
-    public function getConfigitem($sKey){
-        return isset($this->_aConfig[$sKey]) ? $this->_aConfig[$sKey] : false;
-    }    
+    public function getConfigitem(string $sKey): mixed
+    {
+        return $this->_aConfig[$sKey] ?? false;
+    }
     /**
      * get plugin config from its config.json
      * works with
-     *   - shellcmd plugin
+     *   - shellcmd plugins
      * @return array
      */
-    public function getPluginConfig(){
-        if(count($this->_aConfig)){
+    public function getPluginConfig(): array
+    {
+        if (count($this->_aConfig)) {
             return $this->_aConfig;
         }
-        $this->_aConfig=(file_exists($this->_sSelfdir.'/config.json'))
-            ? json_decode(file_get_contents($this->_sSelfdir.'/config.json'), 1)
-            : ["error" => "config.json not found in ".$this->_sSelfdir]
+        $this->_aConfig = (file_exists($this->_sSelfdir . '/config.json'))
+            ? json_decode(file_get_contents($this->_sSelfdir . '/config.json'), 1)
+            : ["error" => "config.json not found in " . $this->_sSelfdir]
         ;
         return $this->_aConfig;
     }
@@ -284,18 +301,12 @@ class ciplugins {
     // ---------------------------------------------------------------
 
     /**
-     * get a location of a plugin file with full path
-     * @param  {bool}  $bAutoload  flag: autoload needed plugin file
+     * Get the classname of a plugin (it is generated by type and plugin name)
      * @return string
      */
-    public function getPluginClassname(){
-        return $this->_sType.'_'.$this->_sPluginname;
-    }
-
-    public function initPlugin__unused(){
-        $sClassname=$this->_sType.'_'.$this->_sPlugindir;
-        $TmpRolloutPlugin = new $sClassname([]);
-
+    public function getPluginClassname(): string
+    {
+        return $this->_sType . '_' . $this->_sPluginname;
     }
 
 }
diff --git a/public_html/deployment/classes/plugins_renderer.class.php b/public_html/deployment/classes/plugins_renderer.class.php
index cd569f54b67f44215f6c6f81f660b03f568e5990..ff8c183374172a028b1ad43b27d81c5464470274 100644
--- a/public_html/deployment/classes/plugins_renderer.class.php
+++ b/public_html/deployment/classes/plugins_renderer.class.php
@@ -17,9 +17,12 @@ require_once('plugins.class.php');
  *
  * 
  * @author axel
+ * 
+ * 2024-08-23  v1.1  Axel Hahn  php8 only; added variable types
  */
-class plugin_renderer extends ciplugins {
-    
+class plugin_renderer extends ciplugins
+{
+
     // ---------------------------------------------------------------
     // 
     // BELOW ARE METHODS FOR A SET SPECIFIC PLUGIN AND TYPE
@@ -30,38 +33,41 @@ class plugin_renderer extends ciplugins {
     // ---------------------------------------------------------------
     // LANGUAGE TEXTS (needed in ui only)
     // ---------------------------------------------------------------
-    
+
     /**
-     * get a translated text from lang_XX.json in plugin dir;
+     * Get a translated text from lang_XX.json in plugin dir;
      * If the key is missed it returns "[KEY :: LANG]"
      * 
      * @see setLang()
      * @param string $sKey  key to find in lang file
      * @return string
      */
-    protected function _t($sKey){
+    protected function _t(string $sKey): string
+    {
         return (isset($this->_aLang[$sKey]) && $this->_aLang[$sKey])
-                ? $this->_aLang[$sKey]
-                : "[ $sKey :: $this->_sLang ]"
+            ? $this->_aLang[$sKey]
+            : "[ $sKey :: $this->_sLang ]"
         ;
     }
 
     /**
-     * set language for output of formdata and other texts.
+     * Set language for output of formdata and other texts.
      * This method loads the language file into a hash. The output of 
      * translated texts can be done with $this->_t("your_key")
      * 
      * @see _t()
+     * 
      * @param string   $sLang  language code, i.e. "de"
      * @return boolean
      */
-    public function setLang($sLang=false){
-        $this->_sLang=$sLang ? $sLang : $this->_sLang;
-        
-        $oReflection=new ReflectionClass($this);
-        $sFile=dirname($oReflection->getFileName()) . '/lang_'.$this->_sLang.'.json';
-        $this->_aLang=(file_exists($sFile)) ? json_decode(file_get_contents($sFile), 1) : $this->_aLang;
-        return true;
+    public function setLang(string $sLang = ''): bool
+    {
+        $this->_sLang = $sLang ?: $this->_sLang;
+
+        $oReflection = new ReflectionClass($this);
+        $sFile = dirname($oReflection->getFileName()) . '/lang_' . $this->_sLang . '.json';
+        $this->_aLang = (file_exists($sFile)) ? json_decode(file_get_contents($sFile), 1) : $this->_aLang;
+        return !!count($this->_aLang);
     }
     // ---------------------------------------------------------------
     // SETTER
@@ -73,44 +79,52 @@ class plugin_renderer extends ciplugins {
     // ---------------------------------------------------------------
 
     /**
-     * for shellcmd plugins: get html code to load javascript file
+     * For shellcmd plugins: get html code to load javascript file
      * The file must exist in the plugin directory
-     * @params  string  $sFile  (basename of) filename, eg. render.js
+     * 
+     * @param  string  $sFile  (basename of) filename, eg. render.js
+     * @return string
      */
-    public function getHtmlLoadScript($sFile){
-        return (file_exists($this->_sSelfdir.'/'.$sFile))
-            ? '<script src="'.$this->_sSelfurl.'/'.$sFile.'"></script>'."\n"
+    public function getHtmlLoadScript(string $sFile): string
+    {
+        return (file_exists($this->_sSelfdir . '/' . $sFile))
+            ? '<script src="' . $this->_sSelfurl . '/' . $sFile . '"></script>' . "\n"
             : ''
         ;
     }
 
     /**
-     * get id for an output div
+     * Get id for an output div
      * @return string
      */
-    public function getHtmlOutId(){
-        return $this->_sPluginname ? 'divPlugin'.$this->_sType.''.$this->_sPluginname : false;
+    public function getHtmlOutId(): string
+    {
+        return $this->_sPluginname ? 'divPlugin' . $this->_sType . '' . $this->_sPluginname : false;
     }
+
     /**
-     * get id for the wrapper div of an output div
+     * Get id for the wrapper div of an output div
      * @return string
      */
-    public function getHtmlOutIdWrapper(){
-        return $this->getHtmlOutId().'Wrapper';
+    public function getHtmlOutIdWrapper(): string
+    {
+        return $this->getHtmlOutId() . 'Wrapper';
     }
 
     /**
-     * get html code for a shellcmd output window
+     * Get html code for a shellcmd output window
+     * @return string The HTML code
      */
-    public function getHtmlOutwindow(){
-        $aConfig=$this->getPluginConfig();
-        return '<div id="'.$this->getHtmlOutIdWrapper().'" class="cmdoutbox">'
-        .'<div id="'.$this->getHtmlOutId().'" '
-        .'class="out'
-            .(isset($aConfig['window-cols'])  && $aConfig['window-cols']  ? ' cmd-cols-'.$aConfig['window-cols'] : ''   )
-            .(isset($aConfig['window-lines']) && $aConfig['window-lines'] ? ' cmd-lines-'.$aConfig['window-lines'] : '' )
-            .'"></div>'
-        .'</div>';
+    public function getHtmlOutwindow(): string
+    {
+        $aConfig = $this->getPluginConfig();
+        return '<div id="' . $this->getHtmlOutIdWrapper() . '" class="cmdoutbox">'
+            . '<div id="' . $this->getHtmlOutId() . '" '
+            . 'class="out'
+            . (isset($aConfig['window-cols']) && $aConfig['window-cols'] ? ' cmd-cols-' . $aConfig['window-cols'] : '')
+            . (isset($aConfig['window-lines']) && $aConfig['window-lines'] ? ' cmd-lines-' . $aConfig['window-lines'] : '')
+            . '"></div>'
+            . '</div>';
     }
 
     // ---------------------------------------------------------------
diff --git a/public_html/deployment/classes/plugins_shellcmd_request.class.php b/public_html/deployment/classes/plugins_shellcmd_request.class.php
index a99ddd34b9d0d834fd601157df73ec1db55ed96c..805378fd732a7bf7f00a34d33763da24c2e9fdc1 100644
--- a/public_html/deployment/classes/plugins_shellcmd_request.class.php
+++ b/public_html/deployment/classes/plugins_shellcmd_request.class.php
@@ -5,37 +5,67 @@
  * 
  * Used in ../plugins/shellcmd/getdata.php
  * 
+ * 2024-08-23  v1.1  Axel Hahn  php8 only; added variable types
  */
 
 require_once('plugins.class.php');
 
-class req_shellcmd {
+class req_shellcmd
+{
 
-    protected $_sPlugin=false;
-    protected $_oPlugin=false;
-    protected $_aReturn=false;
+    /**
+     * plugin name
+     * @var string
+     */
+    protected string $_sPlugin = '';
 
-    protected $_aPluginConfig=[];
+    /**
+     * pligin object
+     * @var object
+     */
+    protected object $_oPlugin;
 
-    protected $_aResult=[];
-    protected $_debug=false;
+    /**
+     * Array of return items
+     * TODO: might be removed
+     * @var array
+    protected array $_aReturn = [];
+     */
 
     /**
-     * plugins class instance
+     * Configuration for the plugin
+     * @var array
+     */
+    protected array $_aPluginConfig = [];
+
+    /**
+     * Result after execution
+     * @var array
+     */
+    protected array $_aResult = [];
+
+    /**
+     * Flag: enable debug? Default: false
+     * @var bool
+     */
+    protected bool $_debug = false;
+
+    /**
+     * ciplugins class instance
      * @var object
      */
-    protected $CI_plugins=false;
+    protected $CI_plugins = false;
 
     // ---------------------------------------------------------------
     // CONSTRUCTOR
     // ---------------------------------------------------------------
 
     /**
-     * constructor
-     * @return bool
+     * Constructor
      */
-    public function __construct(){
-        return $this->detectPlugin();
+    public function __construct()
+    {
+        $this->detectPlugin();
     }
 
     // ---------------------------------------------------------------
@@ -43,39 +73,45 @@ class req_shellcmd {
     // ---------------------------------------------------------------
 
     /**
-     * helper execute a given command and return array with executed
+     * Helper: execute a given command and return array with executed
      * command, returncode, output
+     * @param  string  $sCmd  command to execute
      * @return
      */
-    protected function _execCommand($sCmd){
+    protected function _execCommand(string $sCmd): array
+    {
         exec("$sCmd", $aOut, $iResult);
         return [
-            'command'=>$sCmd,
-            'exitcode'=>$iResult,
-            'time'=>date("H:i:s"),
-            'output'=>$aOut,
+            'command' => $sCmd,
+            'exitcode' => $iResult,
+            'time' => date("H:i:s"),
+            'output' => $aOut,
         ];
     }
 
 
     /**
-     * initialize the shellcmd plugin
+     * Initialize the shellcmd plugin
      * @returm boolean
      */
-    protected function _loadPlugin(){
-        $this->CI_plugins=new ciplugins();
+    protected function _loadPlugin(): bool
+    {
+        $this->CI_plugins = new ciplugins();
         $this->CI_plugins->setPlugin($this->_sPlugin, 'shellcmd');
 
-        $sPluginclass=$this->CI_plugins->getPluginClassname();        
-        $this->_oPlugin=new $sPluginclass();
+        $sPluginclass = $this->CI_plugins->getPluginClassname();
+        $this->_oPlugin = new $sPluginclass();
 
+        return true;
     }
 
     /**
-     * write debug output ... if enabled
+     * Write debug output ... if enabled
+     * @return void
      */
-    protected function _wd($s){
-        echo $this->_debug ? 'DEBUG '.__CLASS__ . ' '.$s."<br>\n" : '';
+    protected function _wd($s): void
+    {
+        echo $this->_debug ? 'DEBUG ' . __CLASS__ . ' ' . $s . "<br>\n" : '';
     }
 
     // ---------------------------------------------------------------
@@ -83,40 +119,46 @@ class req_shellcmd {
     // ---------------------------------------------------------------
 
     /**
-     * detect plugin name to load from GET param "plugin"
+     * Detect plugin name to load from GET param "plugin"
+     * @return bool
      */
-    public function detectPlugin(){
-        $this->_sPlugin=isset($_GET['plugin']) && $_GET['plugin'] ? preg_replace('/^a-z0-9/', '', $_GET['plugin']) : false;
-        $this->_wd("detected plugin: ".$this->_sPlugin);
+    public function detectPlugin(): bool
+    {
+        $this->_sPlugin = isset($_GET['plugin']) && $_GET['plugin'] ? preg_replace('/^a-z0-9/', '', $_GET['plugin']) : false;
+        $this->_wd("detected plugin: " . $this->_sPlugin);
         return true;
     }
 
-
     /**
-     * get data from plugin command and return array with executed
+     * Get data from plugin command and return array with executed
      * command, returncode, output, parsed data
      * @return array
      */
-    public function get(){
+    public function get(): array
+    {
         $this->_loadPlugin();
-        if (!$this->_oPlugin){
-            return $this->_aReturn;
+        if (!$this->_oPlugin) {
+            // return $this->_aReturn;
+            return [];
         }
         $this->CI_plugins->getPluginConfig();
-        $sCmd=$this->CI_plugins->getConfigitem('command');
+        $sCmd = $this->CI_plugins->getConfigitem('command');
         $this->_wd("sCmd=$sCmd");
-        
-        $this->_aResult=$this->_execCommand($sCmd);
-        if (method_exists($this->_oPlugin, "parsedata")){
-            $this->_aResult=$this->_oPlugin->parsedata($this->_aResult);
+
+        $this->_aResult = $this->_execCommand($sCmd);
+        if (method_exists($this->_oPlugin, "parsedata")) {
+            $this->_aResult = $this->_oPlugin->parsedata($this->_aResult);
         }
         return $this->_aResult;
     }
 
     /**
-     * send response as json
+     * Send response as json.
+     * It sends http response header and json data in body
+     * @return void
      */
-    public function sendResponse(){
+    public function sendResponse(): void
+    {
         header('Content-Type: application/json');
         echo json_encode($this->get(), JSON_PRETTY_PRINT);
     }
diff --git a/public_html/deployment/classes/project.class.php b/public_html/deployment/classes/project.class.php
index a1bce66746de6e8ce22a9e01d8f20ab836597d16..9c06d48bdcabeb419c759045f96d44acc12766a3 100644
--- a/public_html/deployment/classes/project.class.php
+++ b/public_html/deployment/classes/project.class.php
@@ -25,6 +25,8 @@ require_once 'htmlguielements.class.php';
 
   ---------------------------------------------------------------------
   2013-11-08  Axel <axel.hahn@iml.unibe.ch>
+  (...)
+  2024-08-28  Axel   php8 only; added variable types; short array syntax
   ###################################################################### */
 
 /**
@@ -41,80 +43,84 @@ class project extends base
      * configuration ($aConfig in the config file)
      * @var array
      */
-    protected $_aConfig = array();
+    protected array $_aConfig = [];
 
     /**
      * configuration of the project (= $aProjects[ID] in the config file)
      * @var array
      */
-    protected $_aPrjConfig = array();
+    protected array $_aPrjConfig = [];
 
     /**
      * version infos of all phases
      * @var array
      */
-    protected $_aData = array();
+    protected array $_aData = [];
 
     /**
      * existing versions in the archive dir
      * @var array
      */
-    protected $_aVersions = array();
+    protected array $_aVersions = [];
 
     /**
      * output file to fetch processing content with ajax request
      * @var string
      */
-    protected $_sProcessTempOut = false;
+    protected string $_sProcessTempOut = '';
 
     /**
      * places of version infos in each deployment phase
      * @var array 
      */
-    protected $_aPlaces = array(
+    protected array $_aPlaces = [
         "onhold" => "Queue",
         "ready2install" => "Puppet",
         "deployed" => "Installiert",
-    );
+    ];
 
     /**
      * collector for returncodes of multiple exec calls
      * @var int
      */
-    protected $_iRcAll = 0;
+    protected int $_iRcAll = 0;
 
 
     /**
      * reference to html renderer class to draw output items
      * @var object
      */
-    protected $_oHtml = false;
+    protected object $_oHtml;
 
     /**
      * object to access a version control, .e. git
      * @var object
      */
-    protected $_oVcs = false;
+    protected object $_oVcs;
 
     /**
      * object for rollout
-     * @var type
+     * @var object
      */
-    public $oRolloutPlugin = false;
+    public object $oRolloutPlugin;
 
-    protected $_sBranchname = false;
+    /**
+     * Name of the current branch
+     * @var string
+     */
+    protected string $_sBranchname = '';
 
     /**
-     * send messages
-     * @var messengerobject
+     * messenger object to send messages
+     * @var object
      */
-    protected $oMessenger = false;
+    protected object $oMessenger;
 
     /**
      * collected errors
      * @var array
      */
-    protected $_errors = [];
+    protected array $_errors = [];
 
     // ----------------------------------------------------------------------
     // constructor
@@ -124,7 +130,7 @@ class project extends base
      * constructor
      * @param string $sId  id of the project
      */
-    public function __construct($sId = false)
+    public function __construct(string $sId = '')
     {
         $this->oUser = new user();
         $this->_oHtml = new htmlguielements();
@@ -144,7 +150,7 @@ class project extends base
      * @param  string $sLevel    warnlevel of the given message
      * @return bool
      */
-    protected function log($sMessage, $sLevel = "info")
+    protected function log(string $sMessage, string $sLevel = "info")
     {
         global $oCLog;
         return $oCLog->add(basename(__FILE__) . " class " . __CLASS__ . " - " . $sMessage, $sLevel);
@@ -157,26 +163,19 @@ class project extends base
      */
     protected function _sendMessage($sMessage)
     {
-        $aConfig = array();
+        $aConfig = [];
 
-        if (
-            array_key_exists('messenger', $this->_aPrjConfig)
-            && array_key_exists('slack', $this->_aPrjConfig['messenger'])
-        ) {
+        if (isset($this->_aPrjConfig['messenger']['slack'])) {
             $sSlack = $this->_aPrjConfig['messenger']['slack'];
-            $aConfig['slack'] = array('incomingurl' => $sSlack);
-            foreach (array('user', 'icon') as $sKey) {
+            $aConfig['slack'] = ['incomingurl' => $sSlack];
+            foreach (['user', 'icon'] as $sKey) {
                 if (isset($this->_aConfig['messenger']['slack']['presets'][$sSlack][$sKey])) {
                     $aConfig['slack'][$sKey] = $this->_aConfig['messenger']['slack']['presets'][$sSlack][$sKey];
                 }
             }
         }
 
-        if (
-            array_key_exists('messenger', $this->_aPrjConfig)
-            && array_key_exists('email', $this->_aPrjConfig['messenger'])
-            && $this->_aPrjConfig['messenger']['email']
-        ) {
+        if (isset($this->_aConfig['messenger']['email'])) {
             $aConfig['email'] = $this->_aConfig['messenger']['email'];
             $aConfig['email']['to'] = $this->_aPrjConfig['messenger']['email'];
         }
@@ -185,7 +184,7 @@ class project extends base
         }
 
         // init on first usage
-        if (!$this->oMessenger) {
+        if (!isset($this->oMessenger) || !$this->oMessenger) {
             $this->oMessenger = new messenger($aConfig);
         }
 
@@ -193,7 +192,7 @@ class project extends base
         $sText = $this->getLabel() . ': ' . html_entity_decode($sMessage) . "\n"
             . t('page-login-username') . ": " . $this->oUser->getUsername() . "\n";
         if (isset($_SERVER) && is_array($_SERVER)) {
-            if (array_key_exists('HTTP_HOST', $_SERVER)) {
+            if (isset($_SERVER['HTTP_HOST'])) {
                 $sText .= t('project-home') . ': ' . $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'] . '/deployment/' . $this->getId() . "\n";
             }
             /*
@@ -202,13 +201,14 @@ class project extends base
         }
              */
         }
-        return $this->oMessenger->sendMessage($sText);
+        $this->oMessenger->sendMessage($sText);
+        return true;
     }
     /**
      * read default config file
      * @return boolean
      */
-    protected function _readConfig()
+    protected function _readConfig(): bool
     {
         global $aConfig;
         $this->_aConfig = $aConfig;
@@ -219,14 +219,14 @@ class project extends base
      * validate config data
      * @return boolean
      */
-    protected function _verifyConfig()
+    protected function _verifyConfig(): bool
     {
         if (!is_array($this->_aPrjConfig) || !count($this->_aPrjConfig)) {
             // die(t("class-project-error-no-config"));
             throw new Exception(t("class-project-error-no-config"));
         }
 
-        if (!array_key_exists("packageDir", $this->_aConfig)) {
+        if (!isset($this->_aConfig["packageDir"])) {
             die(t("class-project-error-no-packagedir"));
         }
         if (!$this->_aConfig["packageDir"]) {
@@ -235,7 +235,7 @@ class project extends base
         if (!file_exists($this->_aConfig["packageDir"])) {
             die(sprintf(t("class-project-error-packagedir-does-not-exist"), $this->_aConfig['packageDir']));
         }
-        if (!array_key_exists("archiveDir", $this->_aConfig)) {
+        if (!isset($this->_aConfig["archiveDir"])) {
             die(t("class-project-error-no-archivedir"));
         }
         if (!$this->_aConfig["archiveDir"]) {
@@ -245,8 +245,8 @@ class project extends base
             die(sprintf(t("class-project-error-packagedir-does-not-exist"), $this->_aConfig['archiveDir']));
         }
 
-        foreach (array("fileprefix", "build", "phases") as $sKey) {
-            if (!array_key_exists($sKey, $this->_aPrjConfig)) {
+        foreach (["fileprefix", "build", "phases"] as $sKey) {
+            if (!isset($this->_aPrjConfig[$sKey])) {
                 die(sprintf(t("class-project-error-missing-prjkey"), $sKey, print_r($this->_aPrjConfig, true)));
             }
         }
@@ -260,7 +260,7 @@ class project extends base
           die(sprintf(t("class-project-error-data-does-not-exist"), $this->_aConfig['dataDir']));
           }
 
-          foreach (array("database", "projects", "sshkeys") as $sKey) {
+          foreach (["database", "projects", "sshkeys"] as $sKey) {
           $sTestDir=$this->_aConfig["dataDir"]."/$sKey";
           if (!file_exists($sTestDir)) {
           mkdir($sTestDir);
@@ -273,10 +273,11 @@ class project extends base
 
     /**
      * execute a commandline; returns a string of output of timestamp, command, output and returncode
-     * @param string $sCommand
+     * @param string $sCommand  command to execute
+     * @param bool   $bFlush    flush content of output buffer
      * @return string
      */
-    protected function _execAndSend($sCommand, $bFlush = false)
+    protected function _execAndSend(string $sCommand, bool $bFlush = false): string
     {
         $this->log(__FUNCTION__ . " start");
         $sReturn = '';
@@ -298,15 +299,15 @@ class project extends base
         $this->log(__FUNCTION__ . " ended command $sCommand");
         $sReturn .= (count($aOutput)) ? htmlentities(implode("\n", $aOutput)) . "\n" : "";
         /*
-          $descriptorspec = array(
-          0 => array("pipe", "r"), // stdin is a pipe that the child will read from
-          1 => array("pipe", "w"), // stdout is a pipe that the child will write to
-          2 => array("pipe", "w")    // stderr is a pipe that the child will write to
-          );
+          $descriptorspec = [
+          0 => ["pipe", "r"], // stdin is a pipe that the child will read from
+          1 => ["pipe", "w"], // stdout is a pipe that the child will write to
+          2 => ["pipe", "w"]    // stderr is a pipe that the child will write to
+          ];
           if ($bFlush) {
           flush();
           }
-          $process = proc_open($sCommand, $descriptorspec, $pipes, realpath('./'), array());
+          $process = proc_open($sCommand, $descriptorspec, $pipes, realpath('./'), []);
 
 
           $sErrors = false;
@@ -353,12 +354,13 @@ class project extends base
     }
 
     /**
-     * add an action log message
-     * @param string $sMessage    message
-     * @param string $sAction     project action
-     * @param string $sLoglevel   loglevel
+     * add an action log message for the current project
+     * @param string $sMessage    message text
+     * @param string $sAction     project action i.e. build, deploy, ...
+     * @param string $sLoglevel   loglevel; default: info
+     * @return void
      */
-    protected function _logaction($sMessage, $sAction = "", $sLoglevel = "info")
+    protected function _logaction(string $sMessage, string $sAction = "", string $sLoglevel = "info"): void
     {
         require_once("actionlog.class.php");
         $oLog = new Actionlog($this->_aConfig["id"]);
@@ -369,7 +371,12 @@ class project extends base
     // GETTER
     // ----------------------------------------------------------------------
 
-    protected function _getConfigFile($sId)
+    /**
+     * Get filename of fonfigfile for this project
+     * @param string $sId  project id
+     * @return string
+     */
+    protected function _getConfigFile(string $sId): string
     {
         if (!$sId) {
             die(t("class-project-error-_getConfigFile-requires-id"));
@@ -378,49 +385,53 @@ class project extends base
     }
 
     /**
-     * get a full ath for temp directory (for a build)
+     * Get a full ath for temp directory (for a build)
      * @return string
      */
-    protected function _getTempDir()
+    protected function _getTempDir(): string
     {
         return $s = $this->_getBuildDir() . '/' . $this->_aPrjConfig["fileprefix"] . "_" . date("Ymd-His");
     }
 
     /**
-     * get full path where the project builds are (a build setes a subdir)
+     * Get full path where the project builds are (a build setes a subdir)
      * @return string
      */
-    protected function _getBuildDir()
+    protected function _getBuildDir(): string
     {
         return $this->_aConfig['buildDir'] . '/' . $this->_aConfig["id"];
     }
 
     /**
-     * get full path where the project default files are
-     * @return type
+     * Get full path where the project default files are
+     * This is an optional step in build() - you can sync default files into 
+     * the build directory. It returns false if the directory with default 
+     * files doesn't exist.
+     * 
+     * @return string|bool
      */
-    protected function _getDefaultsDir()
+    protected function _getDefaultsDir(): string|bool
     {
         $s = $this->_aConfig['buildDefaultsDir'] . '/' . $this->_aConfig["id"];
         return file_exists($s) ? $s : false;
     }
 
     /**
-     * get directory for infofile and package (without extension)
+     * Get directory for infofile and package (without extension)
+     * 
      * @param string $sPhase  one of preview|stage|live ...
      * @param string $sPlace  one of onhold|ready2install|deployed
      * @return string
      */
-    protected function _getFileBase($sPhase, $sPlace)
+    protected function _getFileBase(string $sPhase, string $sPlace): string
     {
-        if (!array_key_exists($sPhase, $this->_aConfig["phases"])) {
+        if (!isset($this->_aConfig["phases"][$sPhase])) {
             die(sprintf(t("class-project-error-wrong-phase"), $sPhase));
         }
-        if (!array_key_exists($sPlace, $this->_aPlaces)) {
+        if (!isset($this->_aPlaces[$sPlace])) {
             die(sprintf(t("class-project-error-wrong-place"), $sPlace));
         }
 
-
         // local file for onhold|ready2install
         $sBase = $this->_aConfig['packageDir'] . "/" . $sPhase . "/" . $this->_aPrjConfig["fileprefix"];
         if (!file_exists($this->_aConfig['packageDir'] . "/" . $sPhase)) {
@@ -433,7 +444,7 @@ class project extends base
         // $sBase .= "/" . $this->_aPrjConfig["fileprefix"];
         // url for deployed
         if ($sPlace == "deployed") {
-            if ($this->isActivePhase($sPhase) && array_key_exists("url", $this->_aPrjConfig["phases"][$sPhase])) {
+            if ($this->isActivePhase($sPhase) && isset($this->_aPrjConfig["phases"][$sPhase]["url"])) {
                 // $sBase = $this->_aPrjConfig["phases"][$sPhase]["url"] . $this->_aPrjConfig["fileprefix"];
                 $sBase = $this->_aPrjConfig["phases"][$sPhase]["url"];
             } else {
@@ -446,48 +457,53 @@ class project extends base
     }
 
     /**
-     * get filename for info/ meta file (.json file)
+     * Get filename for info/ meta file (.json file).
+     * It returns false if the base directory doesn't exist
+     * 
      * @param string $sPhase  one of preview|stage|live ...
      * @param string $sPlace  one of onhold|ready2install|deployed
      * @return string
      */
-    protected function _getInfofile($sPhase, $sPlace)
+    protected function _getInfofile(string $sPhase, string $sPlace): string|bool
     {
         $sBase = $this->_getFileBase($sPhase, $sPlace);
         return $sBase ? $sBase . '/' . $this->_aPrjConfig["fileprefix"] . '.json' : false;
     }
 
     /**
-     * get filename for package file (without file extension)
+     * Get filename for package file (without file extension)
+     * It returns false if the base directory doesn't exist
+     * 
      * @param string $sPhase  one of preview|stage|live ...
      * @param string $sPlace  one of onhold|ready2install|deployed
      * @return string
      */
-    protected function _getPackagefile($sPhase, $sPlace)
+    protected function _getPackagefile(string $sPhase, string $sPlace)
     {
         $sBase = $this->_getFileBase($sPhase, $sPlace);
         return $sBase ? $sBase . '/' . $this->_aPrjConfig["fileprefix"] : false;
     }
 
     /**
-     * list of files of a given phase and place
-     * @param string $sPhase  one of preview|stage|live ...
-     * @param string $sPlace  one of onhold|ready2install|deployed
-     * @return array
+     * Get a list of files of a given phase and place.
+     * It returns false if the base directory doesn't exist
+     * 
+     * @param string $sBase  base directory from where to get files (archive dir of a build)
+     * @return bool|array
      */
-    public function _getBuildfilesByDir($sBase)
+    public function _getBuildfilesByDir(string $sBase): bool|array
     {
-        $aReturn = array();
+        $aReturn = [];
         if (!$sBase || !is_dir($sBase)) {
             return false;
         }
         $iTotalSize = 0;
-        $aReturn = array(
+        $aReturn = [
             'dir' => $sBase,
             'filecount' => false,
             'totalsize' => false,
             'totalsize-hr' => false,
-        );
+        ];
 
         foreach (glob($sBase . '/*') as $sFile) {
             $sFileBase = basename($sFile);
@@ -513,12 +529,12 @@ class project extends base
                     break;
             }
             $iTotalSize += $aStat['size'];
-            $aReturn['files'][$sFileBase] = array(
+            $aReturn['files'][$sFileBase] = [
                 'type' => $sType,
                 'icon' => $this->_oHtml->getIcon($sIcon),
                 'extension' => $sExt,
                 'size' => $aStat['size'],
-            );
+            ];
             $aReturn['types'][$sType][] = $sFileBase;
         }
         $aReturn['totalsize'] = $iTotalSize;
@@ -529,51 +545,61 @@ class project extends base
     }
 
     /**
-     * list of files of a given phase and place
+     * Get a list of files of a given phase and place;
+     * It returns false if the base directory for phase + base doesn't exist
+     * 
      * @param string $sPhase  one of preview|stage|live ...
      * @param string $sPlace  one of onhold|ready2install|deployed
-     * @return array
+     * @return bool|array
      */
-    public function getBuildfilesByPlace($sPhase, $sPlace)
+    public function getBuildfilesByPlace(string $sPhase, string $sPlace): bool|array
     {
         $sBase = $this->_getFileBase($sPhase, $sPlace);
         return $this->_getBuildfilesByDir($sBase);
     }
 
     /**
-     * list of files of a given version number
+     * Get a list of files of a given version number
+     * It returns false if the version directory doesn't exist
+     * 
      * @param string $sVersion  name of version 
      * @return array
      */
-    public function getBuildfilesByVersion($sVersion)
+    public function getBuildfilesByVersion(string $sVersion): bool|array
     {
         return $this->_getBuildfilesByDir($this->_getProjectArchiveDir() . '/' . $sVersion);
     }
 
     /**
-     * get the group id of the project
-     * @return string
+     * Get the group id of the project
+     * It returns false if the group wasn't set
+     * 
+     * @return bool|string
      */
-    public function getProjectGroup()
+    public function getProjectGroup(): bool|string
     {
         return isset($this->_aPrjConfig["projectgroup"]) && $this->_aPrjConfig["projectgroup"] != '-1' ? $this->_aPrjConfig["projectgroup"] : false;
     }
+
     /**
-     * get the group label of the project
-     * @return string
+     * Get the group label (description) of the project
+     * It returns false if the group wasn't set
+     * 
+     * @return bool|string
      */
-    public function getProjectGroupLabel()
+    public function getProjectGroupLabel(): bool|string
     {
         $sGroupid = $this->getProjectGroup();
         return isset($this->_aConfig["projectgroups"][$sGroupid]) ? $this->_aConfig["projectgroups"][$sGroupid] : false;
     }
 
     /**
-     * get full path of a packed project archive
+     * Get full path of a packed project archive
+     * 
      * @param string $sVersion  version number of the build
      * @return string
      */
-    protected function _getArchiveDir($sVersion)
+    protected function _getArchiveDir(string $sVersion): string
     {
         if (!$sVersion) {
             die(t("class-project-error-_getArchiveDir-requires-id"));
@@ -586,10 +612,11 @@ class project extends base
      * - key "ok" anddata
      * or 
      * - key "error" with the message
-     * @param type $sTimestamp
+     * 
+     * @param string $sTimestamp
      * @return array
      */
-    protected function _getArchiveInfos($sTimestamp)
+    protected function _getArchiveInfos(string $sTimestamp): array
     {
         if (!$sTimestamp) {
             die(t("class-project-error-_getArchiveInfos-requires-id"));
@@ -603,7 +630,7 @@ class project extends base
             return $aReturn;
         }
         $aJson = json_decode(file_get_contents($sInfoFile), true);
-        if (is_array($aJson) && array_key_exists("version", $aJson)) {
+        if (is_array($aJson) && isset($aJson["version"])) {
             $aReturn = array_merge($aReturn, $aJson);
             $aReturn['ok'] = 1;
             /*
@@ -621,48 +648,26 @@ class project extends base
     }
 
     /**
-     * get the directory for archive files of this project
+     * Get the directory for archive files of this project
+     * 
      * @return string
      */
-    public function _getProjectArchiveDir()
+    public function _getProjectArchiveDir(): string
     {
         return $this->_aConfig["archiveDir"] . '/' . $this->_aConfig["id"];
     }
 
     /**
-     * TODO: REMOVE
-     * make an http get request and return the response body
-     * @param string $url
-     * @return string
-     */
-    private function _httpGet($url, $iTimeout = 5)
-    {
-        $this->log(__FUNCTION__ . " start");
-        if (!function_exists("curl_init")) {
-            die("ERROR: PHP CURL module is not installed.");
-        }
-        $this->log(__FUNCTION__ . " url: $url");
-        $ch = curl_init($url);
-        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
-        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
-        curl_setopt($ch, CURLOPT_TIMEOUT, $iTimeout);
-        curl_setopt($ch, CURLOPT_USERAGENT, 'IML Deployment GUI');
-        $res = curl_exec($ch);
-        curl_close($ch);
-        $this->log(__FUNCTION__ . " done url: $url");
-        return $res;
-    }
-
-    /**
-     * get all existing versions in archive and its usage
+     * Get all existing versions in archive and its usage
      * versions are array keys; where they are used is written in values
+     * 
      * @return array
      */
-    public function getVersions()
+    public function getVersions(): array
     {
 
         // --- read all file entries
-        $aReturn = array();
+        $aReturn = [];
         $sDir = $this->_getProjectArchiveDir();
         if (is_dir($sDir)) {
             foreach (scandir($sDir) as $sEntry) {
@@ -677,21 +682,23 @@ class project extends base
         foreach ($this->_aData["phases"] as $sPhase => $aData) {
             foreach (array_keys($this->_aPlaces) as $sPlace) {
                 if (isset($aData[$sPlace]["version"])) {
-                    $this->_aVersions[$aData[$sPlace]["version"]][] = array('phase' => $sPhase, 'place' => $sPlace);
+                    $this->_aVersions[$aData[$sPlace]["version"]][] = ['phase' => $sPhase, 'place' => $sPlace];
                 }
             }
         }
         ksort($this->_aVersions);
         return $this->_aVersions;
     }
+
     /**
-     * get an array with all existing build error output files (html)
+     * Get an array with all existing build error output files (html)
+     * 
      * @return array
      */
-    public function getBuildErrors($sProject = false)
+    public function getBuildErrors(string $sProject = ''): array
     {
         // --- read all file entries
-        $aReturn = array();
+        $aReturn = [];
         if (!$sProject) {
             $sProject = $this->_aPrjConfig["fileprefix"] . '_*';
         }
@@ -700,11 +707,14 @@ class project extends base
         }
         return $aReturn;
     }
+
     /**
-     * get an array with all existing build error output files (html)
-     * @return array
+     * Get an array with all existing build error output files (html)
+     * It returns false when path of given logfile contains ".." or the logfile doesn't exist.
+     * 
+     * @return bool|string
      */
-    public function getBuildErrorContent($sLogfile)
+    public function getBuildErrorContent($sLogfile): bool|string
     {
         if (!strpos('..', $sLogfile) === false) {
             return false;
@@ -717,16 +727,17 @@ class project extends base
     }
 
     /**
-     * get Array of all versions, metainfos, in which phases they are in use 
+     * Get an array of all versions, metainfos, in which phases they are in use 
      * and a rollback ist possible or not
+     * 
      * return array
      */
-    protected function _getVersionUsage()
+    protected function _getVersionUsage(): array
     {
-        $aVersionData = array();
+        $aVersionData = [];
         $sLastVersion = false;
         if (!count($this->getVersions())) {
-            return array();
+            return [];
         }
 
         foreach ($this->getVersions() as $sVersion => $aData) {
@@ -755,7 +766,7 @@ class project extends base
                             $bCanRollback = false;
                         }
                         /*
-                          if (!array_key_exists("ok", $aVersionData[$sVersion]["info"])){
+                          if (!isset($aVersionData[$sVersion]["info"]["ok"])){
                           $bCanRollback = false;
                           }
                          */
@@ -769,11 +780,12 @@ class project extends base
     }
 
     /**
-     * recursive delete 
+     * Recursive delete of a given directory
+     * 
      * @param string $dir  directory to delete
-     * @return type
+     * @return bool
      */
-    protected function _rmdir($dir)
+    protected function _rmdir(string $dir): bool
     {
         foreach (scandir($dir) as $sEntry) {
             if (is_dir($dir . '/' . $sEntry) && $sEntry != '.' && $sEntry != '..') {
@@ -785,17 +797,21 @@ class project extends base
     }
 
     /**
-     * cleanup of archive directory; it returns the list of deleted
-     * directories as array
-     * @return array
+     * Cleanup of archive directory; it returns the list of deleted
+     * directories as array.
+     * It returns a string with the error message in case of missing permission
+     * Otherwise it returns an array with all deleted directories.
+     * 
+     * @param bool $bDeleteAll  flag to delete all; default: false = it keeps a few versions
+     * @return string|array
      */
-    public function cleanupArchive($bDeleteAll = false)
+    public function cleanupArchive(bool $bDeleteAll = false): string|array
     {
         if (!$this->oUser->hasPermission("project-action-cleanup")) {
             return $this->oUser->showDenied();
         }
-        $aDelete = array();
-        $aUnused = array();
+        $aDelete = [];
+        $aUnused = [];
 
         $sDir = $this->_getProjectArchiveDir();
         $this->getVersions();
@@ -837,19 +853,21 @@ class project extends base
     }
 
     /**
-     * cleanup of archive directory; it returns the list of deleted
-     * directories as array
-     * @return array
+     * Cleanup of kept build directories (builds with errors) except the last N builds; 
+     * It returns a string with the error message in case of missing permission
+     * Otherwise it returns an array with all deleted directories.
+     * 
+     * @return string|array
      */
-    public function cleanupBuilds()
+    public function cleanupBuilds(): string|array
     {
         $this->log(__FUNCTION__ . " start");
         if (!$this->oUser->hasPermission("project-action-cleanup")) {
             return $this->oUser->showDenied();
         }
         $sDir = $this->_getBuildDir();
-        $aDirlist = array();
-        $aDelete = array();
+        $aDirlist = [];
+        $aDelete = [];
         if (is_dir($sDir)) {
             foreach (scandir($sDir) as $sEntry) {
                 if (is_dir($sDir . '/' . $sEntry) && $sEntry != '.' && $sEntry != '..')
@@ -864,91 +882,102 @@ class project extends base
             if ($this->_rmdir($sDir2)) {
                 $aDelete[] = $sDir2;
             } else {
-                echo t("class-project-warning-cannot-delete-build-dir", $sDir2);
-            };
+                echo sprintf(t("class-project-warning-cannot-delete-build-dir"), $sDir2);
+            }
+            ;
         }
 
         return $aDelete;
     }
 
     /**
-     * cleanup cache of vcs
-     * @param type $iAge
+     * Cleanup cache of vcs
+     * It returns a string with error message in case of missing permission
+     * Otherwise it returns the success as true or false
+     * 
+     * @param int $iAge  max age in sec
+     * @return string|bool
      */
-    public function cleanupVcsCache($iAge = 0)
+    public function cleanupVcsCache($iAge = 0): string|bool
     {
         $this->log(__FUNCTION__ . " start");
         if (!$this->oUser->hasPermission("project-action-cleanup")) {
             return $this->oUser->showDenied();
         }
         $this->_initVcs();
-        if ($this->_oVcs) {
+        if (isset($this->_oVcs) && $this->_oVcs) {
             if (!method_exists($this->_oVcs, "cleanupCache")) {
                 // the version control class does not have this method
                 $this->log(__FUNCTION__ . " sorry, Method cleanupCache does not exist in this VCS class.");
-                return '';
+                return false;
             }
             return $this->_oVcs->cleanupCache($iAge);
         }
+        return false;
     }
 
     /**
-     * get conmplete config of the project
+     * Get conmplete config of the project
+     * 
      * @return array
      */
-    public function getConfig()
+    public function getConfig(): array
     {
         return $this->_aPrjConfig;
     }
 
     /**
-     * get name/ label of the project
+     * Get name/ label of the project
+     * 
      * @return string
      */
-    public function getLabel()
+    public function getLabel(): string
     {
         return isset($this->_aPrjConfig["label"]) ? $this->_aPrjConfig["label"] : '';
     }
 
     /**
-     * get description of the project
+     * Get description of the project
+     * 
      * @return string
      */
-    public function getDescription()
+    public function getDescription(): string
     {
         return isset($this->_aPrjConfig["description"]) ? $this->_aPrjConfig["description"] : '';
     }
 
     /**
-     * get the id of the current project
+     * Get the id of the current project
      * @return string
      */
-    public function getId()
+    public function getId(): string
     {
         return isset($this->_aConfig["id"]) ? $this->_aConfig["id"] : '';
     }
+
     /**
-     * get deploy and queue infos for all phases
+     * Get deploy and queue infos for all phases
      * It build up a subkey "progress" with info if a build is queued
      * or an installation of a new package is going on
+     * 
      * @return array
      */
-    public function getAllPhaseInfos()
+    public function getAllPhaseInfos(): array
     {
 
         $bHasQueue = false;
         $bHasDifferentVersions = false;
         $bFirstVersion = false;
 
-        if (!array_key_exists("phases", $this->_aData)) {
-            $this->_aData["phases"] = array();
+        if (!isset($this->_aData["phases"])) {
+            $this->_aData["phases"] = [];
         }
-        if (!array_key_exists("progress", $this->_aData)) {
-            $this->_aData["progress"] = array();
+        if (!isset($this->_aData["progress"])) {
+            $this->_aData["progress"] = [];
         }
 
         foreach (array_keys($this->_aConfig["phases"]) as $sPhase) {
-            if (!array_key_exists($sPhase, $this->_aData["phases"])) {
+            if (!isset($this->_aData["phases"][$sPhase])) {
                 $this->getPhaseInfos($sPhase);
             }
             // detect progress
@@ -971,39 +1000,40 @@ class project extends base
                 $bHasQueue = true;
             }
         }
-        $this->_aData["progress"] = array(
+        $this->_aData["progress"] = [
             'inprogress' => $bHasDifferentVersions,
             'hasQueue' => $bHasQueue,
-        );
+        ];
         return $this->_aData["phases"];
     }
 
     /**
-     * get statusinfos of a named phase
+     * Get statusinfos of a named phase
+     * 
      * @param string $sPhase name of the phase; one of preview|stage|live
      * @return array
      */
-    public function getPhaseInfos($sPhase)
+    public function getPhaseInfos(string $sPhase): array
     {
         if (!$sPhase) {
             die(t("class-project-error-getPhaseInfos-requires-phase"));
         }
-        if (!array_key_exists("phases", $this->_aData))
-            $this->_aData["phases"] = array();
+        if (!isset($this->_aData["phases"]))
+            $this->_aData["phases"] = [];
 
-        if (!array_key_exists($sPhase, $this->_aData["phases"])) {
+        if (!isset($this->_aData["phases"][$sPhase])) {
             if ($this->isActivePhase($sPhase)) {
 
-                $this->_aData["phases"][$sPhase] = array();
-                $aTmp = array();
+                $this->_aData["phases"][$sPhase] = [];
+                $aTmp = [];
 
                 // a blocked package is waiting for deployment timeslot?
                 $sKey = "onhold";
                 $sJsonfile = $this->_getInfofile($sPhase, $sKey);
-                $aTmp[$sKey] = array();
+                $aTmp[$sKey] = [];
                 if (file_exists($sJsonfile)) {
                     $aJson = json_decode(file_get_contents($sJsonfile), true);
-                    if (array_key_exists("version", $aJson)) {
+                    if (isset($aJson["version"])) {
                         $aTmp[$sKey] = $aJson;
                         $aTmp[$sKey]["infofile"] = $sJsonfile;
                         $aTmp[$sKey]["ok"] = 1;
@@ -1018,12 +1048,12 @@ class project extends base
                 // package for puppet
                 $sKey = "ready2install";
                 $sJsonfile = $this->_getInfofile($sPhase, $sKey);
-                $aTmp[$sKey] = array();
+                $aTmp[$sKey] = [];
                 if (file_exists($sJsonfile)) {
                     // $sPkgfile = $this->_getPackagefile($sPhase, $sKey);
                     // if (file_exists($sPkgfile)) {
                     $aJson = json_decode(file_get_contents($sJsonfile), true);
-                    if (is_array($aJson) && array_key_exists("version", $aJson)) {
+                    if (isset($aJson["version"])) {
                         $aTmp[$sKey] = $aJson;
                         $aTmp[$sKey]["infofile"] = $sJsonfile;
                         // $aTmp[$sKey]["packagefile"] = $sPkgfile;
@@ -1041,7 +1071,7 @@ class project extends base
                 // published data
                 $sKey = "deployed";
                 $sJsonfile = $this->_getInfofile($sPhase, $sKey);
-                $aTmp[$sKey] = array();
+                $aTmp[$sKey] = [];
 
                 // use version cache
                 require_once(__DIR__ . '/../../valuestore/classes/valuestore.class.php');
@@ -1051,11 +1081,11 @@ class project extends base
                 // echo "Place: <pre>" . print_r($oVersion->whereiam(), 1) . "</pre>";
                 // echo "Versionen: <pre>" . print_r($aVersions, 1) . "</pre>";
                 if (count($aVersions)) {
-                    $aTmp[$sKey] = array();
+                    $aTmp[$sKey] = [];
                     $aTmp[$sKey] = $aVersions[0]['_data'];
                     $aTmp[$sKey]["infofile"] = '[versioncache]';
 
-                    $aTmp[$sKey]['_hosts'] = array();
+                    $aTmp[$sKey]['_hosts'] = [];
                     foreach ($aVersions as $sHostname => $aHostdata) {
                         $aTmp[$sKey]['_hosts'][$aHostdata['host']] = $aHostdata;
                     }
@@ -1099,12 +1129,13 @@ class project extends base
      * </code>
      * returns<br>
      * Array ( [0] => project1 [1] => project2 ) 
+     * 
      * @param string $sort sort by "id" (default) or "label"
      * @return array
      */
-    public function getProjects($sort = 'id')
+    public function getProjects(string $sort = 'id'): array
     {
-        $aReturn = array();
+        $aReturn = [];
         foreach (glob(dirname($this->_getConfigFile("dummy")) . "/*.json") as $filename) {
             $aReturn[] = str_replace(".json", "", basename($filename));
         }
@@ -1128,11 +1159,12 @@ class project extends base
     }
 
     /**
-     * check if the given phase is active for this project
-     * @param type $sPhase
-     * @return type
+     * Check if the given phase is active for this project
+     * 
+     * @param string $sPhase name of the phase; one of preview|stage|live
+     * @return bool
      */
-    public function isActivePhase($sPhase)
+    public function isActivePhase(string $sPhase): bool
     {
         return (
             $this->_aPrjConfig && isset($this->_aPrjConfig["phases"][$sPhase]["active"][0])
@@ -1142,29 +1174,29 @@ class project extends base
     }
 
     /**
-     * return array of all (active and inactive) phases
+     * Get array of all (active and inactive) phases
      * @return array
      */
-    public function getPhases()
+    public function getPhases(): array
     {
         return $this->_aConfig["phases"];
     }
 
     /**
-     * return array of all (active and inactive) phases
+     * Get array of all (active and inactive) phases
      * @return array
      */
-    public function getPlaces()
+    public function getPlaces(): array
     {
         return $this->_aPlaces;
     }
     /**
-     * get a flat array with active phases of the project
+     * Get a flat array with active phases of the project
      * @return array
      */
-    public function getActivePhases()
+    public function getActivePhases(): array
     {
-        $aReturn = array();
+        $aReturn = [];
         foreach (array_keys($this->_aConfig["phases"]) as $s) {
             if ($this->isActivePhase($s)) {
                 $aReturn[] = $s;
@@ -1176,11 +1208,12 @@ class project extends base
     /**
      * find the next active phase of a project
      * @param string $sPhase current phase; if empty the function sends back the first phase
+     * @return string
      */
-    public function getNextPhase($sPhase = false)
+    public function getNextPhase(string $sPhase = ''): string
     {
         if ($sPhase) {
-            if (!array_key_exists($sPhase, $this->_aConfig["phases"])) {
+            if (!isset($this->_aConfig["phases"][$sPhase])) {
                 die(sprintf(t("class-project-error-wrong-phase"), $sPhase));
             }
         }
@@ -1204,21 +1237,25 @@ class project extends base
     }
 
     /**
-     * get an array with deploy status ...  
+     * Get an array with deploy status ...  
      *    'inprogress'=>do versions differ from phase to phase = rollout of a version is in progress
           'hasQueue'=>is there a package in a queue (waiting for deployment time to get ready to be installed)
      * @return array
      */
-    public function getProgress()
+    public function getProgress(): array
     {
         $this->getAllPhaseInfos();
         return $this->_aData['progress'];
     }
     /**
      * check: is the deployment to the next phase enabled for this phase?
-     * @param type $sPhase  current phase
+     * It returns a string when current user has no permissions.
+     * Otherwise it returns true or false.
+     * 
+     * @param string $sPhase  current phase
+     * @return string|bool
      */
-    public function canAcceptPhase($sPhase = false)
+    public function canAcceptPhase(string $sPhase = ''): string|bool
     {
         if (
             !$this->oUser->hasPermission("project-action-accept") && !$this->oUser->hasPermission("project-action-accept-$sPhase")
@@ -1231,7 +1268,7 @@ class project extends base
             // for better performance: skip check on overview page
             /*
               $aRepodata = $this->getRepoRevision();
-              if (!array_key_exists("revision", $aRepodata)) {
+              if (!isset($aRepodata["revision"])) {
               return false;
               }
              */
@@ -1240,7 +1277,7 @@ class project extends base
         }
 
 
-        if (!array_key_exists($sPhase, $this->_aConfig["phases"])) {
+        if (!isset($this->_aConfig["phases"][$sPhase])) {
             die(sprintf(t("class-project-error-wrong-phase"), $sPhase));
         }
         if (!$this->isActivePhase($sPhase)) {
@@ -1258,7 +1295,14 @@ class project extends base
         // array key "ok" must be in the ready2install and deployed info
         // and a version must be installed
         if (
-            array_key_exists($sPhase, $this->_aData["phases"]) && array_key_exists("onhold", $this->_aData["phases"][$sPhase]) && array_key_exists("ready2install", $this->_aData["phases"][$sPhase]) && array_key_exists("deployed", $this->_aData["phases"][$sPhase]) && array_key_exists("ok", $this->_aData["phases"][$sPhase]["onhold"]) && array_key_exists("ok", $this->_aData["phases"][$sPhase]["ready2install"]) && array_key_exists("ok", $this->_aData["phases"][$sPhase]["deployed"]) && array_key_exists("version", $this->_aData["phases"][$sPhase]["deployed"])
+            isset($this->_aData["phases"][$sPhase])
+            && isset($this->_aData["phases"][$sPhase]["onhold"])
+            && isset($this->_aData["phases"][$sPhase]["ready2install"])
+            && isset($this->_aData["phases"][$sPhase]["deployed"])
+            && isset($this->_aData["phases"][$sPhase]["onhold"]["ok"])
+            && isset($this->_aData["phases"][$sPhase]["ready2install"]["ok"])
+            && isset($this->_aData["phases"][$sPhase]["deployed"]["ok"])
+            && isset($this->_aData["phases"][$sPhase]["deployed"]["version"])
         ) {
             return true;
         }
@@ -1266,45 +1310,46 @@ class project extends base
     }
 
     /**
-     * get list of remote branches and tags
+     * Get list of remote branches and tags.
+     * It returns false if the VCS was not initialize or has no method getRemoteBranches()
+     * 
      * @param bool $bIgnoreCache  flag to ignore exiting cached data
-     * @return array|boolean
+     * @return bool|array
      */
-    public function getRemoteBranches($bIgnoreCache = false)
+    public function getRemoteBranches(bool $bIgnoreCache = false): bool|array
     {
         $this->log(__FUNCTION__ . "($bIgnoreCache) start");
         $this->_initVcs();
-        if ($this->_oVcs) {
+        if (isset($this->_oVcs) && $this->_oVcs) {
             if (!method_exists($this->_oVcs, "getRemoteBranches")) {
                 // the version control class does not have this method
-                return '';
+                return false;
             }
             return $this->_oVcs->getRemoteBranches($bIgnoreCache);
         }
         return false;
     }
 
-
-
     /**
-     * get current revision and log message from remote repo
+     * Get current revision and log message from remote repo
+     * 
      * @param boolean  $bRefresh  optional: refresh data; default: use cache
      * @return array
      */
-    public function getRepoRevision($bRefresh = false)
+    public function getRepoRevision(bool $bRefresh = false): array
     {
         $this->log(__FUNCTION__ . "($bRefresh) start");
 
         if (!$this->_aPrjConfig["build"]["type"]) {
-            $this->_aData["phases"]["source"] = array("error" => t("class-project-error-repo-type-not-set"),);
+            $this->_aData["phases"]["source"] = ["error" => t("class-project-error-repo-type-not-set"),];
         } else {
             $this->_initVcs();
-            if ($this->_oVcs) {
+            if (isset($this->_oVcs) && $this->_oVcs) {
                 $this->_aData["phases"]["source"] = $this->_oVcs->getRepoRevision($bRefresh);
             } else {
-                $this->_aData["phases"]["source"] = array(
+                $this->_aData["phases"]["source"] = [
                     "error" => sprintf(t("class-project-error-repo-type-not-supported"), $this->_aPrjConfig["build"]["type"]),
-                );
+                ];
             }
         }
         $this->log(__FUNCTION__ . " result:<pre>" . print_r($this->_aData, 1) . "</pre>");
@@ -1312,20 +1357,20 @@ class project extends base
     }
 
     /**
-     * init version control system (git, ...)
-     * @return vcs-object
+     * Initialize version control system (git, ...) if it is not initialized yet
+     * it sets the object $this->_oVcs
      */
-    protected function _initVcs()
+    protected function _initVcs(): void
     {
         $this->log(__FUNCTION__ . " start");
-        if (!$this->_oVcs) {
+        if (!isset($this->_oVcs)) {
             if (!$this->_aPrjConfig["build"]["type"]) {
-                $this->_aData["phases"]["source"] = array("error" => t("class-project-error-repo-type-not-set"),);
+                $this->_aData["phases"]["source"] = ["error" => t("class-project-error-repo-type-not-set"),];
             } else {
                 if (!@include_once("vcs." . $this->_aPrjConfig["build"]["type"] . ".class.php")) {
-                    $this->_aData["phases"]["source"] = array(
+                    $this->_aData["phases"]["source"] = [
                         "error" => sprintf(t("class-project-error-repo-type-not-supported"), $this->_aPrjConfig["build"]["type"]),
-                    );
+                    ];
                 } else {
                     $aConfig = $this->_aPrjConfig["build"];
                     // for vcs classes
@@ -1341,17 +1386,17 @@ class project extends base
                 }
             }
         }
-        return $this->_oVcs;
+        // return $this->_oVcs;
     }
 
     /**
-     * get an array of enabled plugins
+     * Get an array of enabled plugins
      * @param string  $sSection  one of false|"rollout"|...
      * @return array
      */
-    public function getConfiguredPlugins($sSection = false)
+    public function getConfiguredPlugins(string $sSection = ''): array
     {
-        $aReturn = array();
+        $aReturn = [];
         if (!$sSection) {
             $aReturn = $this->_aConfig["plugins"];
         } else {
@@ -1363,23 +1408,24 @@ class project extends base
     }
 
     /**
-     * get a location of a plugin file with full path
+     * Get a location of a plugin file with full path
+     * 
      * @param string  $sType         type of plugin, i.e. "rollout"
      * @param string  $sPluginName   Name of plugin
      * @return string
      */
-    protected function _getPluginFilename($sType, $sPluginName)
+    protected function _getPluginFilename(string $sType, string $sPluginName): string
     {
         return __DIR__ . '/../plugins/' . $sType . '/' . $sPluginName . '/' . $sType . '_' . $sPluginName . '.php';
     }
 
     /**
-     * get a flat array of all existing ssh keys
+     * Get a flat array of all existing ssh keys
      * @return array
      */
-    protected function _getSshKeys()
+    protected function _getSshKeys(): array
     {
-        $aReturn = array();
+        $aReturn = [];
         foreach (glob($this->_aConfig["dataDir"] . "/sshkeys/*.pub") as $filename) {
             $aReturn[] = str_replace(".pub", "", basename($filename));
         }
@@ -1388,25 +1434,27 @@ class project extends base
     }
 
     /**
-     * get a flat array with regexes of deploy times
+     * Get a flat array with regexes of deploy times.
+     * It returns false if the given phase is not active
+     * 
      * @param string $sPhase  phase
-     * @return array
+     * @return bool|array
      */
-    protected function _getDeploytimes($sPhase)
+    protected function _getDeploytimes(string $sPhase): bool|array
     {
         if (!$this->isActivePhase($sPhase)) {
             $sError = sprintf(t("class-project-warning-phase-not-active"), $sPhase);
             $this->_logaction($sError, __FUNCTION__, "error");
             return false;
         }
-        $aDeploytimes = array();
-        if (array_key_exists("deploytimes", $this->_aConfig["phases"][$sPhase])) {
-            $aDeploytimes = $this->_aConfig["phases"][$sPhase]["deploytimes"];
-        }
+
+        $aDeploytimes = $this->_aConfig["phases"][$sPhase]["deploytimes"] ?? [];
+
         if (
-            array_key_exists("deploytimes", $this->_aPrjConfig["phases"][$sPhase]) && $this->_aPrjConfig["phases"][$sPhase]["deploytimes"]
+            isset($this->_aPrjConfig["phases"][$sPhase]["deploytimes"])
+            && $this->_aPrjConfig["phases"][$sPhase]["deploytimes"]
         ) {
-            $aDeploytimes = array($this->_aPrjConfig["phases"][$sPhase]["deploytimes"]);
+            $aDeploytimes = [$this->_aPrjConfig["phases"][$sPhase]["deploytimes"]];
         }
         return $aDeploytimes;
     }
@@ -1415,7 +1463,16 @@ class project extends base
     // SETTER
     // ----------------------------------------------------------------------
 
-    protected function _setProcessOutFile($sNewTempfile = false)
+    /**
+     * Generate a filename for the process output.
+     * The last output file will be deleted if it exists.
+     * It returns false if no pram for new basename was given.
+     * Otherwise it returns a filename with full path in temp folder.
+     * 
+     * @param string $sNewTempfile
+     * @return bool|string
+     */
+    protected function _setProcessOutFile(string $sNewTempfile = ''): bool|string
     {
         if ($this->_sProcessTempOut && file_exists($this->_sProcessTempOut)) {
             unlink($this->_sProcessTempOut);
@@ -1426,21 +1483,23 @@ class project extends base
     }
 
     /**
-     * get projects from ldap; it returns ldap search items with cn as 
+     * Get projects from ldap; it returns ldap search items with cn as 
      * array key.
+     * It returns false if no result was found
      * 
-     * @return array
+     * @param string  $sSearchFilter  LDAP search filter
+     * @return bool|array
      */
-    protected function _ldapProjectSearch($sSearchFilter)
+    protected function _ldapProjectSearch(string $sSearchFilter): bool|array
     {
-        $aReturn = array();
+        $aReturn = [];
         require_once("ldap.class.php");
         $oLdapIML = new imlldap($this->_aConfig['projects']['ldap']);
         // $oLdapIML->debugOn();
         $aResultsIml = $oLdapIML->searchDn(
             $this->_aConfig['projects']['ldap']['DnProjects'],
             $sSearchFilter,
-            array("*")
+            ["*"]
         );
         if (!$aResultsIml['count']) {
             return false;
@@ -1450,13 +1509,13 @@ class project extends base
         /*
           unset($aResultsIml['count']);
           foreach ($aResultsIml as $aItem) {
-          $aReturn[$aItem['cn'][0]] = array(
+          $aReturn[$aItem['cn'][0]] = [
           'dn' => $aItem['dn'],
           'cn' => $aItem['cn'][0],
           '_description' => $aItem['description'][0],
           'title' => $sTitle,
           'description' => $sDescription,
-          );
+          ];
           }
           $oLdapIML->close();
           ksort($aReturn);
@@ -1467,10 +1526,12 @@ class project extends base
     }
 
     /**
-     * load config of a project
-     * @return boolean
+     * Load config of a project with given project id and turn a bool for success
+     * 
+     * @param  string $sId  new project id of project to load
+     * @return boolean Success
      */
-    public function setProjectById($sId)
+    public function setProjectById(string $sId): bool
     {
         if ($sId !== preg_replace('/[^a-z0-9\-\_]/i', '', $sId)) {
             $this->_errors[] = sprintf(t("class-project-error-config-wrongid"), htmlentities($sId));
@@ -1497,9 +1558,7 @@ class project extends base
             $sQuery = '(&(objectclass=hieraSource)(documentIdentifier=' . $sId . '))';
             $aResult = $this->_ldapProjectSearch($sQuery);
             // echo '<pre>$aResult = ' . print_r($aResult, 1) . '</pre>';
-            if (
-                is_array($aResult) && $aResult[0] && array_key_exists('hieradata', $aResult[0])
-            ) {
+            if (isset($aResult[0]['hieradata'])) {
                 foreach ($aResult[0]['hieradata'] as $sLine) {
                     // echo $sLine.'<br>';
                     if (preg_match('/^cfg=/', $sLine)) {
@@ -1522,16 +1581,16 @@ class project extends base
         $sPluginName = (isset($this->_aPrjConfig['deploy']['enabled_rollout_plugin']) && $this->_aPrjConfig['deploy']['enabled_rollout_plugin'])
             ? $this->_aPrjConfig['deploy']['enabled_rollout_plugin']
             : 'default';
-        $this->oRolloutPlugin = false;
+        unset($this->oRolloutPlugin);
         try {
             require_once $this->_getPluginFilename('rollout', $sPluginName);
             $sPluginClassname = 'rollout_' . $sPluginName;
-            $this->oRolloutPlugin = new $sPluginClassname(array(
+            $this->oRolloutPlugin = new $sPluginClassname([
                 'lang' => $this->_aConfig['lang'],
                 'phase' => false,
                 'globalcfg' => isset($this->_aConfig['plugins']['rollout'][$sPluginName]) ? $this->_aConfig['plugins']['rollout'][$sPluginName] : [],
                 'projectcfg' => $this->_aPrjConfig,
-            ));
+            ]);
             // print_r($this->_oRolloutPlugin->getPluginfos());
             // print_r($this->_oRolloutPlugin->getName());
         } catch (Exception $ex) {
@@ -1542,12 +1601,12 @@ class project extends base
     /**
      * set a branchname
      * @param string $sBranchname name of the branch, i.e. "origin/master"
-     * @return bool
+     * @return string
      */
-    public function setBranchname($sBranchname)
+    public function setBranchname(string $sBranchname): string
     {
         $this->_sBranchname = $sBranchname;
-        if ($this->_oVcs) {
+        if (isset($this->_oVcs) && $this->_oVcs) {
             if (method_exists($this->_oVcs, "setCurrentBranch")) {
                 $this->_oVcs->setCurrentBranch($sBranchname);
             }
@@ -1560,12 +1619,14 @@ class project extends base
     // ----------------------------------------------------------------------
 
     /**
-     * send data to a tempfile for ajax polling
-     * @param type $sTmpFile
-     * @param type $sData
-     * @return boolean
+     * Store data to a tempfile (read by for ajax polling) and update actions box
+     * 
+     * @param  string  $sData     full output of all so far executed shell commands
+     * @param  array   $aActions  for right output box: Array of actions with marker of current action
+     *                            see build() method for the structure
+     * @return bool|int
      */
-    protected function _TempFill($sData, $aActions = array())
+    protected function _TempFill(string $sData, array $aActions = []): bool|int
     {
         if (!$this->_sProcessTempOut) {
             return false;
@@ -1592,12 +1653,13 @@ class project extends base
     }
 
     /**
-     * delete tempfile for ajax polling; if a directory is given as parameter
-     * the tmp file will be moved there 
+     * Delete tempfile for ajax polling; if a directory is given as parameter
+     * the tmp file will be moved there.
+     * 
      * @param string  $sTempDir  optional; target dir to copy; default=false (=delete file)
      * @return boolean
      */
-    protected function _TempDelete($sTempDir = false)
+    protected function _TempDelete(string $sTempDir = ''): bool
     {
         if (!$this->_sProcessTempOut) {
             return false;
@@ -1613,15 +1675,16 @@ class project extends base
     }
 
     /**
-     * get the name of the current branch (or default branch)
-     * @param bool  $bReturnMasterIfEmpty  flag: if there is no current branch then detect a master branch
-     * @return string
+     * Get the name of the current branch (or default branch).
+     * It returns false if the vcs was not initialized yet.
+     * 
+     * @return string|bool
      */
-    public function getBranchname($bReturnMasterIfEmpty = false)
+    public function getBranchname(): string|bool
     {
         $this->log(__FUNCTION__ . " start");
         $this->_initVcs();
-        if ($this->_oVcs) {
+        if (isset($this->_oVcs) && $this->_oVcs) {
             if (method_exists($this->_oVcs, "getCurrentBranch")) {
                 $sCurrentBranch = $this->_oVcs->getCurrentBranch(true); // true means search for master branch if empty
                 if ($sCurrentBranch) {
@@ -1637,10 +1700,11 @@ class project extends base
      * Build a new package for the deployment. It will be put to the queue
      * of the first active phase (i.e. preview).
      * If there is no deployment time range it will be deployed too.
-     * @global type $aParams
-     * @return boolean|string
+     * 
+     * @global string $sTmpFile
+     * @return boolean|string false or HTML code
      */
-    public function build($sTmpFile = false)
+    public function build(string $sTmpFile = ''): bool|string
     {
         $this->log(__FUNCTION__ . " start");
         if (!$this->oUser->hasPermission("project-action-build")) {
@@ -1649,22 +1713,22 @@ class project extends base
         global $aParams;
         $sReturn = false;
 
-        $aActionList = array(
+        $aActionList = [
             'iActive' => 0,
             'label' => t('build'),
-            'actions' => array(
-                array('label' => t('class-project-build-label-cleanup-builds')),
-                array('label' => t('class-project-build-label-create-workdir')),
-                array('label' => t('class-project-build-label-get-sources-from-version-control')),
-                array('label' => t('class-project-build-label-execute-hook-postclone')),
-                array('label' => t('class-project-build-label-copy-default-structure')),
-                array('label' => t('class-project-build-label-execute-hook-precompress')),
-                array('label' => t('class-project-build-label-cleanup-project')),
-                array('label' => t('class-project-build-label-create-package')),
-                array('label' => t('class-project-build-label-remove-workdir')),
-                array('label' => t('class-project-build-label-queue-to-first-active-phase')),
-            ),
-        );
+            'actions' => [
+                ['label' => t('class-project-build-label-cleanup-builds')],
+                ['label' => t('class-project-build-label-create-workdir')],
+                ['label' => t('class-project-build-label-get-sources-from-version-control')],
+                ['label' => t('class-project-build-label-execute-hook-postclone')],
+                ['label' => t('class-project-build-label-copy-default-structure')],
+                ['label' => t('class-project-build-label-execute-hook-precompress')],
+                ['label' => t('class-project-build-label-cleanup-project')],
+                ['label' => t('class-project-build-label-create-package')],
+                ['label' => t('class-project-build-label-remove-workdir')],
+                ['label' => t('class-project-build-label-queue-to-first-active-phase')],
+            ],
+        ];
         $this->_setProcessOutFile($sTmpFile);
 
         $this->_iRcAll = 0;
@@ -1684,7 +1748,7 @@ class project extends base
         $this->_TempFill($sReturn, $aActionList);
 
         $this->_initVcs();
-        if (!$this->_oVcs) {
+        if (!isset($this->_oVcs) || !$this->_oVcs) {
             $sError = sprintf(t('class-project-error-build-type-not-supported'), $this->_aPrjConfig["build"]["type"]);
             $this->_logaction($sError, __FUNCTION__, "error");
             return $this->_oHtml->getBox("error", $sError . $sReturn);
@@ -1723,10 +1787,10 @@ class project extends base
 
         $sReturn .= '<pre>' . $this->_oVcs->getSources($sTempBuildDir) . '</pre>';
 
-        $aRepodata=$this->getRepoRevision();
-        $sRevisionShort=substr($aRepodata['revision'], 0, 8);
+        $aRepodata = $this->getRepoRevision();
+        $sRevisionShort = substr($aRepodata['revision'], 0, 8);
 
-        $sReturn .= $this->_oHtml->getBox("info", t('commitmessage') . '<pre>'.htmlentities($aRepodata['message']).'</pre>');
+        $sReturn .= $this->_oHtml->getBox("info", t('commitmessage') . '<pre>' . htmlentities($aRepodata['message']) . '</pre>');
         $sReturn .= $this->_execAndSend("ls -lisa $sTempBuildDir");
 
         if (!$this->_iRcAll == 0) {
@@ -1757,24 +1821,24 @@ class project extends base
             # /task#5047
 
         }
-        $aCivars=[
-            'branch'=>$aRepodata['branch'],
-            'branch_short'=>$aRepodata['shortname'],
-            'branch_type'=>$aRepodata['type'],
-            'revision'=>$aRepodata['revision'],
-            'revision_short'=>$sRevisionShort,
-            'imagepart'=>$aRepodata['type']=='tags' 
-                    ? $aRepodata['shortname']
-                    : $sRevisionShort
-                    ,
+        $aCivars = [
+            'branch' => $aRepodata['branch'],
+            'branch_short' => $aRepodata['shortname'],
+            'branch_type' => $aRepodata['type'],
+            'revision' => $aRepodata['revision'],
+            'revision_short' => $sRevisionShort,
+            'imagepart' => $aRepodata['type'] == 'tags'
+                ? $aRepodata['shortname']
+                : $sRevisionShort
+            ,
             // 'message'=>$aRepodata['message'],
         ];
 
         $sCfgContent .= "\n"
             . "# ---------- generated CI SERVER variables\n"
             . "\n"
-            ;
-        foreach ($aCivars as $sKey => $value){
+        ;
+        foreach ($aCivars as $sKey => $value) {
             $sCfgContent .= "export CI_$sKey=\"$value\"; \n";
         }
 
@@ -1860,7 +1924,7 @@ class project extends base
         // cleanup .git, .svn, ...
         // --------------------------------------------------
         $sReturn .= '<h3>' . t('class-project-build-label-cleanup-project') . '</h3>';
-        if ($this->_oVcs) {
+        if (isset($this->_oVcs) && $this->_oVcs) {
             $this->_oVcs->cleanupWorkdir($sTempBuildDir);
         }
 
@@ -1875,13 +1939,10 @@ class project extends base
         // --------------------------------------------------
         $sReturn .= '<h3>' . t('class-project-build-label-create-package') . '</h3>';
         // public_html must exist
-        if (
-            array_key_exists('haspublic', $this->_aPrjConfig["build"])
-            && $this->_aPrjConfig["build"]["haspublic"][0]
-        ) {
+        if (isset($this->_aPrjConfig["build"]['haspublic'][0])) {
             $sWebroot = false;
-            $sWebroot1 = $sTempBuildDir . '/public_html';
-            $sWebroot2 = $sTempBuildDir . '/public';
+            $sWebroot1 = "$sTempBuildDir/public_html";
+            $sWebroot2 = "$sTempBuildDir/public";
             if (file_exists($sWebroot1)) {
                 $sWebroot = $sWebroot1;
             }
@@ -1914,20 +1975,20 @@ class project extends base
         $sInfoFileArchiv = $this->_getArchiveDir($sTs2) . '/' . basename($this->_getInfofile($sFirstLevel, "deployed"));
         $sPackageFileArchiv = $this->_getArchiveDir($sTs2) . '/' . basename($this->_getPackagefile($sFirstLevel, "deployed"));
 
-        $aInfos = array(
+        $aInfos = [
             'date' => $sTs,
             'version' => $sTs2,
             // 'branch' => $sBranch,
-            'branch'=>$aRepodata['branch'],
-            'branch_short'=>$aRepodata['shortname'],
-            'branch_type'=>$aRepodata['type'],
-            'revision'=>$aRepodata['revision'],
-            'revision_short'=>$sRevisionShort,
+            'branch' => $aRepodata['branch'],
+            'branch_short' => $aRepodata['shortname'],
+            'branch_type' => $aRepodata['type'],
+            'revision' => $aRepodata['revision'],
+            'revision_short' => $sRevisionShort,
 
-            'imagepart'=>$aCivars['imagepart'],
+            'imagepart' => $aCivars['imagepart'],
 
             'message' => $aRepodata['message'],
-        );
+        ];
         /*
           "user": "' . $aParams["inputUser"] . '",
           "remark": "' . $aParams["inputComment"] . '"
@@ -1966,16 +2027,16 @@ class project extends base
             try {
                 include_once $this->_getPluginFilename('build', $sPluginName);
                 $sPluginClassname = 'build_' . $sPluginName;
-                $oPlugin = new $sPluginClassname(array(
+                $oPlugin = new $sPluginClassname([
                     'lang' => $this->_aConfig['lang'],
                     'workdir' => $sTempBuildDir,
                     'outfile' => $sPackageFileArchiv,
-                ));
+                ]);
             } catch (Exception $ex) {
                 return $this->_oHtml->getBox(
                     "error",
                     "FAILED to initialize build plugin " . $sPluginName . '<br>'
-                        . $sReturn
+                    . $sReturn
                 );
             }
 
@@ -2040,23 +2101,24 @@ class project extends base
     }
 
     /**
-     * put a packaged version into the queue of a specified phase
-     * @param string $sPhase    name of the phase
+     * Put a packaged version into the queue of a specified phase
+     * 
+     * @param string $sPhase    name of the phase that gets the new version
      * @param string $sVersion  version 
-     * @return string
+     * @return string The HTML code
      */
-    public function queue($sPhase, $sVersion)
+    public function queue(string $sPhase, string $sVersion): string
     {
-        $aActionList = array(
+        $aActionList = [
             'iActive' => 0,
             'label' => t("queue"),
-            'actions' => array(
-                array('label' => t("class-project-queue-label-checks")),
-                array('label' => t("class-project-queue-label-remove-existing-version")),
-                array('label' => t("class-project-queue-label-link-new-version")),
-                array('label' => t("class-project-queue-label-deploy")),
-            ),
-        );
+            'actions' => [
+                ['label' => t("class-project-queue-label-checks")],
+                ['label' => t("class-project-queue-label-remove-existing-version")],
+                ['label' => t("class-project-queue-label-link-new-version")],
+                ['label' => t("class-project-queue-label-deploy")],
+            ],
+        ];
         $this->_logaction(t('starting') . " queue($sPhase, $sVersion)", __FUNCTION__);
         $sReturn = "<h2> " . t("queue") . " " . $this->getLabel() . " :: $sPhase</h2>";
         $this->_TempFill($sReturn, $aActionList);
@@ -2124,14 +2186,15 @@ class project extends base
     }
 
     /**
-     * deploy a queued package - this moves the queue into the repo directory
-     * and will be installed on server within 30 min.
+     * Deploy a queued package - this moves the queue into the repo directory.
      * This method checks the deploy times
-     * @param string $sPhase which queue of which phase we want to install in server
+     * It returns the output to show in browser
+     * 
+     * @param string $sPhase              the queue of which phase we want to install in server
      * @param bool   $bIgnoreDeploytimes  flag; if true it will override time windows
-     * @return boolean|string
+     * @return string The HTML output
      */
-    public function deploy($sPhase, $bIgnoreDeploytimes = false)
+    public function deploy(string $sPhase, bool $bIgnoreDeploytimes = false): string
     {
         $this->log(__FUNCTION__ . " start");
         if (
@@ -2139,16 +2202,16 @@ class project extends base
         ) {
             return $this->oUser->showDenied();
         }
-        $aActionList = array(
+        $aActionList = [
             'iActive' => 0,
             'label' => t("deploy"),
-            'actions' => array(
-                array('label' => t("class-project-deploy-label-checks")),
-                array('label' => t("class-project-deploy-label-activate-queued-version")),
-                array('label' => t("class-project-deploy-label-synch-packages")),
-                array('label' => t("class-project-deploy-label-install-on-target")),
-            ),
-        );
+            'actions' => [
+                ['label' => t("class-project-deploy-label-checks")],
+                ['label' => t("class-project-deploy-label-activate-queued-version")],
+                ['label' => t("class-project-deploy-label-synch-packages")],
+                ['label' => t("class-project-deploy-label-install-on-target")],
+            ],
+        ];
         $sReturn = "<h2>" . t("deploy") . " " . $this->getLabel() . " :: $sPhase</h2>";
         $this->_TempFill($sReturn, $aActionList);
 
@@ -2244,10 +2307,10 @@ class project extends base
         // synch packages
         // --------------------------------------------------
         // $sReturn.=$this->_execAndSend("ln -s $sLinkTarget $sLinkName");
-        if (array_key_exists('mirrorPackages', $this->_aConfig) && count($this->_aConfig['mirrorPackages'])) {
+        if (isset($this->_aConfig['mirrorPackages']) && count($this->_aConfig['mirrorPackages'])) {
             foreach ($this->_aConfig['mirrorPackages'] as $sLabel => $aTarget) {
                 $sReturn .= '<h3>' . sprintf(t("class-project-info-deploy-synching-package"), $sLabel) . "</h3>";
-                if (array_key_exists('type', $aTarget)) {
+                if (isset($aTarget['type'])) {
                     $sCmd = false;
                     // $sSource=$this->_aConfig["packageDir"]."/$sPhase/*";
                     $sSource = $sRepoLink;
@@ -2280,7 +2343,7 @@ class project extends base
         // --------------------------------------------------
         // run action to install
         // --------------------------------------------------
-        $sDeploymethod = array_key_exists("deploymethod", $this->_aPrjConfig["phases"][$sPhase]) ? $this->_aPrjConfig["phases"][$sPhase]["deploymethod"] : "none";
+        $sDeploymethod = isset($this->_aPrjConfig["phases"][$sPhase]["deploymethod"]) ? $this->_aPrjConfig["phases"][$sPhase]["deploymethod"] : "none";
         // $sTargethosts = array_key_exists("hosts", $this->_aPrjConfig["phases"][$sPhase]) ? $this->_aPrjConfig["phases"][$sPhase]["hosts"] : '';
 
         $sReturn .= '<h3>' . t("class-project-info-deploy-start-by-method") . ' :: ' . $sDeploymethod . '</h3>'
@@ -2347,12 +2410,13 @@ class project extends base
     }
 
     /**
-     * accept a the installed version in a phase and put this version
+     * Accept a the installed version of the given phase and put this version
      * to the queue of the next phase.
-     * @param string $sPhase which queue of which phase we want to install in server
-     * @return type
+     * 
+     * @param  string  $sPhase  phase to accept to be rolled out on the next phase
+     * @return string The HTML code
      */
-    public function accept($sPhase)
+    public function accept(string $sPhase): string
     {
         $this->log(__FUNCTION__ . " start");
         if (
@@ -2388,22 +2452,26 @@ class project extends base
     }
 
     /**
-     * save POSTed data as project config
-     * @return boolean
+     * Save data as project config.
+     * If no data were given then $_POST is used.
+     * It returns bool with success state or a string with deny error message
+     * 
+     * @param  array  $aData  optional: data to write
+     * @return boolean|string
      */
-    public function saveConfig($aData = false)
+    public function saveConfig(array $aData = []): bool|string
     {
         $this->log(__FUNCTION__ . " start");
         if (!$this->oUser->hasPermission("project-action-setup")) {
             return $this->oUser->showDenied();
         }
         $this->_logaction(t('starting') . " saveConfig(...)", __FUNCTION__);
-        if (!$aData) {
+        if (!count($aData)) {
             $aData = $_POST;
         }
 
-        foreach (array('id', 'label', 'description', 'contact', 'build', 'fileprefix', 'phases') as $sKey) {
-            if (!array_key_exists($sKey, $aData)) {
+        foreach (['id', 'label', 'description', 'contact', 'build', 'fileprefix', 'phases'] as $sKey) {
+            if (!isset($aData[$sKey])) {
                 $this->_logaction(t('abortet') . " missing key $sKey in savedata", __FUNCTION__, "error");
                 return false;
             }
@@ -2411,8 +2479,8 @@ class project extends base
         $sId = $aData["id"];
 
         // remove unwanted items
-        foreach (array("setupaction", "prj", "id") as $s) {
-            if (array_key_exists($s, $aData)) {
+        foreach (["setupaction", "prj", "id"] as $s) {
+            if (isset($aData[$s])) {
                 unset($aData[$s]);
             }
         }
@@ -2435,17 +2503,17 @@ class project extends base
 
 
             $sDn = 'documentIdentifier=' . $sId . ',' . $this->_aConfig['projects']['ldap']['DnProjects'];
-            $aItem = array(
-                'objectClass' => array(
+            $aItem = [
+                'objectClass' => [
                     'document',
                     'hieraSource',
                     'top',
-                ),
-                'hieraData' => array(
+                ],
+                'hieraData' => [
                     'cfg=' . json_encode($aData),
                     'updated=' . date("Y-m-d H:i:s") . ' by ' . $this->oUser->getUsername(),
-                )
-            );
+                ]
+            ];
 
             require_once("ldap.class.php");
             $oLdapIML = new imlldap($this->_aConfig['projects']['ldap']);
@@ -2482,12 +2550,13 @@ class project extends base
     }
 
     /**
-     * create a new project; it returns the error message if it fails and
+     * Create a new project; it returns the error message if it fails and
      * an empty string if it was successful.
+     * 
      * @param string  $sId  id
      * @return string
      */
-    public function create($sId)
+    public function create(string $sId): string
     {
         $this->log(__FUNCTION__ . " start");
         if (!$this->oUser->hasPermission("project-action-create")) {
@@ -2520,24 +2589,24 @@ class project extends base
         $this->_readConfig();
         $this->_aConfig["id"] = $sId;
 
-        $this->_aPrjConfig = array(
+        $this->_aPrjConfig = [
             "id" => $sId, // for saveConfig
             "label" => "$sId",
             "fileprefix" => "$sId",
             "description" => '',
             "contact" => '',
-            "build" => array(
+            "build" => [
                 "type" => "",
                 "ssh" => "",
                 "auth" => "",
                 "webaccess" => "",
-            ),
-            "phases" => array(
-                "preview" => array(),
-                "stage" => array(),
-                "live" => array(),
-            ),
-        );
+            ],
+            "phases" => [
+                "preview" => [],
+                "stage" => [],
+                "live" => [],
+            ],
+        ];
         $this->_verifyConfig(); // check skeleton
         $bReturn = $this->saveConfig($this->_aPrjConfig);
         if (!$bReturn) {
@@ -2552,11 +2621,15 @@ class project extends base
     }
 
     /**
-     * delete a project; it returns a string with errormessage; false = no error
-     * @param array $aOptions
-     * @return boolean|string
+     * Delete a project; it returns a string with errormessage; empty string = no error
+     * 
+     * @param array $aOptions  array with enabled actions
+     *                         - bRemoveRepolinks
+     *                         - bRemoveArchive
+     *                         - bRemoveConfig
+     * @return string
      */
-    public function delete($aOptions = array())
+    public function delete(array $aOptions = []): string
     {
         $this->log(__FUNCTION__ . " start");
         if (!$this->oUser->hasPermission("project-action-delete")) {
@@ -2568,9 +2641,9 @@ class project extends base
         }
         $this->_logaction(t('starting') . " delete()", __FUNCTION__);
 
-        // (array("bRemoveRepolinks", "bRemoveArchive", "bRemoveConfig")
+        // ["bRemoveRepolinks", "bRemoveArchive", "bRemoveConfig"]
         // --- remove links in phases directory to built archives
-        if (array_key_exists("bRemoveRepolinks", $aOptions) && $aOptions["bRemoveRepolinks"]) {
+        if (isset($aOptions["bRemoveRepolinks"]) && $aOptions["bRemoveRepolinks"]) {
             echo "DELETE Repo-Links ...<br>";
 
             foreach (array_keys($this->getPhases()) as $sPhase) {
@@ -2587,11 +2660,11 @@ class project extends base
                 }
             }
         }
-        if (array_key_exists("bRemoveArchive", $aOptions) && $aOptions["bRemoveArchive"]) {
+        if (isset($aOptions["bRemoveArchive"]) && $aOptions["bRemoveArchive"]) {
             echo "DELETE built Archives ...<br>";
             $this->cleanupArchive(true); // true to delete all
         }
-        if (array_key_exists("bRemoveConfig", $aOptions) && $aOptions["bRemoveConfig"]) {
+        if (isset($aOptions["bRemoveConfig"]) && $aOptions["bRemoveConfig"]) {
             echo "DELETE Config ...<br>";
             // echo "config file: $sCfgfile<br>";
             if (file_exists($sCfgfile . ".ok")) {
@@ -2609,6 +2682,6 @@ class project extends base
         }
         $this->_sendMessage(t('finished') . " delete()");
         $this->_logaction(t('finished') . " delete()", __FUNCTION__, "success");
-        return false;
+        return '';
     }
 }
diff --git a/public_html/deployment/classes/project_gui.class.php b/public_html/deployment/classes/project_gui.class.php
index de11b83d2598dfd5d299af5d0caccc22412479ba..d6fef25337b20a041746c2bc9d7a85386e50b422 100644
--- a/public_html/deployment/classes/project_gui.class.php
+++ b/public_html/deployment/classes/project_gui.class.php
@@ -13,76 +13,79 @@ require_once 'htmlguielements.class.php';
 
   ---------------------------------------------------------------------
   2013-11-08  Axel <axel.hahn@iml.unibe.ch>
+  (...)
+  2024-08-26  Axel   php8 only; added variable types; short array syntax
   ###################################################################### */
 
 /**
  * class for single project
  */
 // class project {
-class projectgui extends project {
-
-    /**
-     * constructor
-     * @param string $sId  id of the project
-    public function __construct($sId = false) {
-        parent::__construct($sId);
-        $this->_oHtml = new htmlguielements();
-    }
-    */
+class projectgui extends project
+{
 
     // ----------------------------------------------------------------------
     // private functions
     // ----------------------------------------------------------------------
 
     /**
-     * return html code for a div with background color based on a checksum of the given text
-     * @param string $sText      text that is used for checksum; if false ist returns a gray
+     * Return html code for a div with background color based on a checksum of the given text
+     * 
+     * @param null|string $sText      text that is used for checksum; if false ist returns a gray
      * @param string $sContent   optional: text to show
-     * @return string
+     * @return string The HTML code
      */
-    private function _getChecksumDiv($sText, $sContent='', $sBarHeight='3px') {
-        if ($sText){
-            
+    private function _getChecksumDiv(null|string $sText = '', string $sContent = '', string $sBarHeight = '3px'): string
+    {
+        if ($sText) {
+
             // color ranges in decimal values for RGB from ... to
-            $iFgStart=60;  $iFgEnd=160;
-            $iBgStart=200; $iBgEnd=250;
+            $iFgStart = 60;
+            $iFgEnd = 160;
+            $iBgStart = 200;
+            $iBgEnd = 250;
 
-            $iFgStart=60;  $iFgEnd=160;
-            $iBgStart=190; $iBgEnd=250;
+            $iFgStart = 60;
+            $iFgEnd = 160;
+            $iBgStart = 190;
+            $iBgEnd = 250;
 
             // deivider: 3 digits of md5 will be extracted
-            $iFgDivider=16*16*16/($iFgEnd-$iFgStart);
-            $iBgDivider=16*16*16/($iBgEnd-$iBgStart);
-            
-            $sHash=md5($sText);
-            $sColor=''
-                . 'color: rgba(' 
-                . ($iFgStart + round(hexdec(substr($sHash,0,3))/$iFgDivider)) . ','
-                . ($iFgStart + round(hexdec(substr($sHash,3,3))/$iFgDivider)) . ','
-                . ($iFgStart + round(hexdec(substr($sHash,6,3))/$iFgDivider)) . ','
+            $iFgDivider = 16 * 16 * 16 / ($iFgEnd - $iFgStart);
+            $iBgDivider = 16 * 16 * 16 / ($iBgEnd - $iBgStart);
+
+            $sHash = md5($sText);
+            $sColor = ''
+                . 'color: rgba('
+                . ($iFgStart + round(hexdec(substr($sHash, 0, 3)) / $iFgDivider)) . ','
+                . ($iFgStart + round(hexdec(substr($sHash, 3, 3)) / $iFgDivider)) . ','
+                . ($iFgStart + round(hexdec(substr($sHash, 6, 3)) / $iFgDivider)) . ','
                 . '1'
                 . ');'
-                . 'background: rgba(' 
-                . ($iBgStart + round(hexdec(substr($sHash,0,3))/$iBgDivider)) . ','
-                . ($iBgStart + round(hexdec(substr($sHash,3,3))/$iBgDivider)) . ','
-                . ($iBgStart + round(hexdec(substr($sHash,6,3))/$iBgDivider)) . ','
+                . 'background: rgba('
+                . ($iBgStart + round(hexdec(substr($sHash, 0, 3)) / $iBgDivider)) . ','
+                . ($iBgStart + round(hexdec(substr($sHash, 3, 3)) / $iBgDivider)) . ','
+                . ($iBgStart + round(hexdec(substr($sHash, 6, 3)) / $iBgDivider)) . ','
                 . '1'
                 . ');'
-                ;
+            ;
         } else {
             $sColor = "color: #888; background: #ccc;";
         }
-        return '<div style="' . $sColor . ' border-top: '.$sBarHeight.' solid;">'.($sContent ? $sContent : ' ').'</div>';
+        return '<div style="' . $sColor . ' border-top: ' . $sBarHeight . ' solid;">' . ($sContent ?: ' ') . '</div>';
     }
 
 
     /**
-     * get html code for the colored bar on top of each phase detail items
+     * Get html code for the colored bar on top of each phase detail items.
+     * It returns false of revision number was not found in the given phase + place
+     * 
      * @param string $sPhase  phase of a project
      * @param string $sPlace  place in the given phase
-     * @return string
+     * @return bool|string The HTML code
      */
-    private function _renderBar($sPhase, $sPlace, $sBarHeight='3px') {
+    private function _renderBar(string $sPhase, string $sPlace, string $sBarHeight = '3px'): bool|string
+    {
         $aDataPhase = $this->getPhaseInfos($sPhase);
         $aData = $aDataPhase[$sPlace];
         if (!array_key_exists("revision", $aData)) {
@@ -91,39 +94,47 @@ class projectgui extends project {
         return $this->_getChecksumDiv($aData["revision"], '', $sBarHeight);
     }
 
-    private function _renderHostsData($aData) {
+    /**
+     * Render deploy infos: show hosts and its installed revisions
+     * @param array $aData  deployment metadata 
+     * @return string
+     */
+    private function _renderHostsData(array $aData): string
+    {
         $sReturn = '';
-        if (array_key_exists('_hosts', $aData)) {
-            
+        if (isset($aData['_hosts'])) {
+
             // $sReturn.= print_r($aData['_hosts'], 1);
-            $sReturn.= '<div class="hosts">'
-                    . '<br><strong>' . t('hosts') . ':</strong><br>'
+            $sReturn .= '<div class="hosts">'
+                . '<br><strong>' . t('hosts') . ':</strong><br>'
             ;
             foreach ($aData['_hosts'] as $sHostname => $aHostinfos) {
                 $oUpdateDate = date("U", strtotime($aHostinfos['time']));
                 $iAgeUpdate = round((date("U") - $oUpdateDate) / 60);
-                $sAge = $iAgeUpdate < 60 * 60 * 13 ? $iAgeUpdate . " min" : "??";
-
-                $sReturn.= '<div class="host">'
-                        . $this->_getChecksumDiv(
-                            $aHostinfos['_data']['revision'],
-                            $this->_oHtml->getIcon('host').'<br>' . $sHostname
-                        )
-                        . "($sAge)"
-                        . '</div>'
+                $sAge = $iAgeUpdate < 60 * 60 * 13 ? "$iAgeUpdate min" : "??";
+
+                $sReturn .= '<div class="host">'
+                    . $this->_getChecksumDiv(
+                        $aHostinfos['_data']['revision'],
+                        $this->_oHtml->getIcon('host') . '<br>' . $sHostname
+                    )
+                    . "($sAge)"
+                    . '</div>'
                 ;
             }
-            $sReturn.= '</div><div style="clear: both;"></div>';
+            $sReturn .= '</div><div style="clear: both;"></div>';
         }
         return $sReturn;
     }
 
     /**
-     * get html code for list of hosts in a phase
+     * Get html code for list of hosts in a phase
+     * 
      * @param string $sPhase  phase of a project
      * @return string
      */
-    private function _renderHosts($sPhase) {
+    private function _renderHosts(string $sPhase): string
+    {
         $aDataPhase = $this->getPhaseInfos($sPhase);
         if (is_array($aDataPhase) && array_key_exists('deployed', $aDataPhase)) {
             return $this->_renderHostsData($aDataPhase['deployed']);
@@ -132,25 +143,27 @@ class projectgui extends project {
     }
 
     /**
-     * get html code for list of files in a phase
+     * Get html code for list of files in a phase
+     * 
      * @param string $sPhase  phase of a project
      * @return string
      */
-    private function _renderFiles($sPhase) {
+    private function _renderFiles(string $sPhase): string
+    {
         $sReturn = '';
         $aFiles = $this->getBuildfilesByPlace($sPhase, 'ready2install');
         if (!$aFiles || !$aFiles['filecount']) {
             return '';
         }
-        $sReturn.='<strong>' . t("filelist") . '</strong> (' . $aFiles['filecount'] . '):<br>';
+        $sReturn .= '<strong>' . t("filelist") . '</strong> (' . $aFiles['filecount'] . '):<br>';
         foreach ($aFiles['files'] as $sFilename => $aData) {
-            $sReturn.='<div class="file file-' . $aData['type'] . ' fileext-' . $aData['extension'] . '" title="' . $sFilename . ' (' . $aData['type'] . ')">'
-                    . $aData['icon'] . $sFilename
-                    // . ' ('.$aData['type'].')'
-                    . '</div>'
+            $sReturn .= '<div class="file file-' . $aData['type'] . ' fileext-' . $aData['extension'] . '" title="' . $sFilename . ' (' . $aData['type'] . ')">'
+                . $aData['icon'] . $sFilename
+                // . ' ('.$aData['type'].')'
+                . '</div>'
             ;
         }
-        $sReturn.='(' . $aFiles['totalsize-hr'] . ')';
+        $sReturn .= '(' . $aFiles['totalsize-hr'] . ')';
         return $sReturn;
     }
 
@@ -160,56 +173,57 @@ class projectgui extends project {
 
 
     /**
-     * render html for a row with td for all places of a phase
+     * Get html for a row with td for all places of a phase
+     * 
      * @param string $sPhase   phase
      * @param bool   $bActions draw action links (deploy, accept) on/ off
      * @param bool   $bLong    use long variant to display infos? 
-     * @return string|boolean
+     * @return string
      */
-    public function renderAllPhaseDetails($sPhase, $bActions = true, $bLong = true) {
-        if (!$sPhase) {
-            return false;
-        }
+    public function renderAllPhaseDetails(string $sPhase, bool $bActions = true, bool $bLong = true): string
+    {
         if (!$this->isActivePhase($sPhase)) {
             return '
                         <td class="td-phase-' . $sPhase . ' td-phase-inactive ' . $this->_aConfig["id"] . '" colspan="' . count($this->_aPlaces) . '">
-                            <div class="versioninfo center inactive">' . $this->_oHtml->getIcon('sign-info').t('inactive') . '</div>
+                            <div class="versioninfo center inactive">' . $this->_oHtml->getIcon('sign-info') . t('inactive') . '</div>
                         </td>';
         }
         $sRow2 = false;
 
-        $aRows = array();
+        $aRows = [];
         $sLastPlace = '';
-        
+
         foreach (array_keys($this->_aPlaces) as $sPlace) {
             $aRows[$sPlace] = $this->renderPhaseDetail($sPhase, $sPlace, $bActions, $bLong);
-            
+
             // generate ">>" sign for lastly generated td
-            if ($sLastPlace && array_key_exists("version", $this->_aData["phases"][$sPhase][$sLastPlace]) 
-                    && array_key_exists("version", $this->_aData["phases"][$sPhase][$sPlace]) 
-                    && $this->_aData["phases"][$sPhase][$sLastPlace]["version"] == $this->_aData["phases"][$sPhase][$sPlace]["version"] 
-                    && !$bLong
+            if (
+                $sLastPlace && array_key_exists("version", $this->_aData["phases"][$sPhase][$sLastPlace])
+                && array_key_exists("version", $this->_aData["phases"][$sPhase][$sPlace])
+                && $this->_aData["phases"][$sPhase][$sLastPlace]["version"] == $this->_aData["phases"][$sPhase][$sPlace]["version"]
+                && !$bLong
             ) {
                 $aRows[$sLastPlace] = $this->_renderBar($sPhase, $sPlace) . "&raquo;";
             }
             $sLastPlace = $sPlace;
         }
-        
+
         foreach (array_keys($this->_aPlaces) as $sPlace) {
-            $sRow2.='<td class=" td-phase-'.$sPhase.' td-place-'.$sPlace.' td' . $this->_aConfig["id"] . '">' . $aRows[$sPlace] . '</td>';
+            $sRow2 .= '<td class=" td-phase-' . $sPhase . ' td-place-' . $sPlace . ' td' . $this->_aConfig["id"] . '">' . $aRows[$sPlace] . '</td>';
         }
         return $sRow2;
     }
 
     /**
-     * return html code for current project errors by rendering a box per error in $this->_errors
+     * Get html code for current project errors by rendering a box per error in $this->_errors
      * @return string
      */
-    public function renderErrorBoxes(){
-        $sReturn='';
-        if(count($this->_errors)){
-            foreach($this->_errors as $sError){
-                $sReturn.=$this->_oHtml->getBox("error", $sError);
+    public function renderErrorBoxes(): string
+    {
+        $sReturn = '';
+        if (count($this->_errors)) {
+            foreach ($this->_errors as $sError) {
+                $sReturn .= $this->_oHtml->getBox("error", $sError);
             }
         }
         return $sReturn;
@@ -220,22 +234,25 @@ class projectgui extends project {
      * fix output of commit message as html
      * This is a compatibility function for older builds
      * 
-     * @param  string  $sMessage  git commit message
+     * @param  null|string  $sMessage  git commit message
      * @return string
      */
-    public function transformCommitMessage($sMessage){
-        if(strstr($sMessage, '<br>Date:')){
-            $_aReplace=[
+    public function transformCommitMessage(null|string $sMessage): string
+    {
+        if (strstr($sMessage, '<br>Date:')) {
+            $_aReplace = [
                 '<br>Author:' => "\nAuthor:",
                 '<br>Date:' => "\nDate:",
                 '<br><br>' => "\n\n",
             ];
-            $sMessage=str_replace(array_keys($_aReplace), array_values($_aReplace), $sMessage)." *";
+            $sMessage = str_replace(array_keys($_aReplace), array_values($_aReplace), $sMessage) . " *";
         }
         return htmlentities($sMessage);
     }
+
     /**
-     * render html code for info link that shows popup with metadata on mouseover
+     * Get html code for info link that shows popup with metadata on mouseover
+     * 
      * @param array $aInfos   metainfos of the package (from json file)
      *                   one of ok=1|error=message - status key
      *                   date      - timestamp of build
@@ -249,63 +266,65 @@ class projectgui extends project {
      *                   hpos  - horizontal position; one of left|right; default: right
      * @return string
      */
-    public function renderInfoLink($aInfos, $aOptions = array()) {
+    public function renderInfoLink(array $aInfos, array $aOptions = []): string
+    {
         $sReturn = '';
         $bIsError = false;
         $this->_oHtml = new htmlguielements();
 
-        $sInfos='';
-        $sTitle='';
+        $sInfos = '';
+        $sTitle = '';
         if (array_key_exists("title", $aOptions) && $aOptions["title"]) {
-            $sTitle.=$aOptions["title"];
+            $sTitle .= $aOptions["title"];
         }
         if (array_key_exists("ok", $aInfos)) {
             $sLinktitle = t('infos');
             if (array_key_exists("message", $aInfos)) {
-                $sInfos.=$this->_getChecksumDiv($aInfos["revision"],
-                        $this->_oHtml->getIconByType('calendar') . t('build-from') . ' ' . date("d.m.Y H:i:s", strtotime($aInfos["date"])) . '<br>'
-                        . $this->_oHtml->getIconByType('branch') . t('branch') . ': ' . $aInfos["branch"] . '<br>'
-                        . $this->_oHtml->getIconByType('revision') . t('revision') . ': ' . $this->_renderRevision($aInfos["revision"]) . '<br>'
-                        . $this->_oHtml->getIconByType('comment') . t('commitmessage') . ': '
-                        )
-                        . '<pre>' . $this->transformCommitMessage($aInfos["message"]) . '</pre>';
+                $sInfos .= $this->_getChecksumDiv(
+                    $aInfos["revision"],
+                    $this->_oHtml->getIconByType('calendar') . t('build-from') . ' ' . date("d.m.Y H:i:s", strtotime($aInfos["date"])) . '<br>'
+                    . $this->_oHtml->getIconByType('branch') . t('branch') . ': ' . $aInfos["branch"] . '<br>'
+                    . $this->_oHtml->getIconByType('revision') . t('revision') . ': ' . $this->_renderRevision($aInfos["revision"]) . '<br>'
+                    . $this->_oHtml->getIconByType('comment') . t('commitmessage') . ': '
+                )
+                    . '<pre>' . $this->transformCommitMessage($aInfos["message"]) . '</pre>';
                 if (array_key_exists("more", $aOptions)) {
-                    $sInfos.=$aOptions["more"];
+                    $sInfos .= $aOptions["more"];
                 }
             }
         } else {
             $bIsError = true;
             if (!$sTitle) {
-                $sTitle.=' ' . t('error');
+                $sTitle .= ' ' . t('error');
             }
             $sLinktitle = t('error');
-            $sInfos = $this->_oHtml->getBox('error', '') . '<p>'.$aInfos["error"].'</p>';
+            $sInfos = $this->_oHtml->getBox('error', '') . '<p>' . $aInfos["error"] . '</p>';
         }
-        $sInfos.=$this->_renderHostsData($aInfos, '');
+        $sInfos .= $this->_renderHostsData($aInfos);
 
         if (array_key_exists("label", $aOptions) && $aOptions["label"]) {
-            $sLinktitle.=$aOptions["label"];
+            $sLinktitle .= $aOptions["label"];
         }
 
         // render html
         $sId = 'info' . md5($sInfos);
         $sReturn = '<a href="#" class="btn ' . ($bIsError ? 'btn-danger' : 'btn-default') . '" title="" 
-                onclick="showIdAsModalMessage(\'' . $sId . '\', \''.$sTitle.'\'); return false;"
+                onclick="showIdAsModalMessage(\'' . $sId . '\', \'' . $sTitle . '\'); return false;"
                 >'
-                . $this->_oHtml->getIcon($bIsError ? 'sign-error' : 'sign-info') // ... '<i class="fa fa-info"></i> '
-                . $sLinktitle
-                . '</a><div id="' . $sId . '" style="display: none;" '
-                ;
+            . $this->_oHtml->getIcon($bIsError ? 'sign-error' : 'sign-info') // ... '<i class="fa fa-info"></i> '
+            . $sLinktitle
+            . '</a><div id="' . $sId . '" style="display: none;" '
+        ;
         if (array_key_exists("hpos", $aOptions)) {
-            $sReturn.=' class="' . $aOptions["hpos"] . '"';
+            $sReturn .= ' class="' . $aOptions["hpos"] . '"';
         }
-        $sReturn.='>';
+        $sReturn .= '>';
 
         if ($sTitle) {
             // $sReturn.='<span class="title">' . $sTitle . '</span><br><br>';
         }
 
-        $sReturn.=$sInfos . '</div>';
+        $sReturn .= $sInfos . '</div>';
 
         if ($bIsError) {
             // $sReturn = '<div class="error">' . $sReturn . '</div>';
@@ -315,52 +334,61 @@ class projectgui extends project {
     }
 
     /**
-     * render html for a colored link to any project action
+     * Get html for a colored link to any project action
+     * 
      * @param string $sFunction name of the action; one of accept|build|cleanup|deploy|new|overview|phase|rollback|setup
      * @param string $sPhase    current phase where to place the link
      * @return string
      */
-    public function renderLink($sFunction, $sPhase = false, $sVersion = false) {
+    public function renderLink(string $sFunction, string $sPhase = '', string $sVersion = ''): string
+    {
         $sFirst = $this->getNextPhase();
         $sNext = $this->getNextPhase($sPhase);
-        $aLinkdata = array(
-            'default' => array('class' => ''),
-            'accept' => array('class' => $sNext,
+        $aLinkdata = [
+            'default' => ['class' => ''],
+            'accept' => [
+                'class' => $sNext,
                 'hint' => sprintf(t("accept-hint"), $sPhase, $sNext),
                 'label' => t('accept'),
-            ),
-            'build' => array('class' => $sFirst,
+            ],
+            'build' => [
+                'class' => $sFirst,
                 'hint' => sprintf(t("build-hint"), $sFirst),
                 'label' => t('build'),
                 'role' => 'buildProject'
-            ),
-            'cleanup' => array('class' => ''),
-            'deploy' => array('class' => $sPhase,
+            ],
+            'cleanup' => ['class' => ''],
+            'deploy' => [
+                'class' => $sPhase,
                 'hint' => sprintf(t("deploy-hint"), $sPhase),
                 'label' => t('deploy'),
-            ),
-            'new' => array(
+            ],
+            'new' => [
                 'hint' => t("new-project-hint"),
                 'label' => t('new-project'),
-            ),
-            'overview' => array('class' => '',
+            ],
+            'overview' => [
+                'class' => '',
                 'hint' => t('menu-project-home') . ' [' . $this->getLabel() . ']',
                 'label' => $this->getLabel()
-            ),
-            'phase' => array('icon' => $this->_oHtml->getIcon('phase'), 'class' => $sPhase,
+            ],
+            'phase' => [
+                'icon' => $this->_oHtml->getIcon('phase'),
+                'class' => $sPhase,
                 'hint' => sprintf(t('phase-details-hint'), $sPhase),
                 'label' => sprintf(t('phase-details'), $sPhase),
-            ),
-            'rollback' => array('class' => $sPhase,
+            ],
+            'rollback' => [
+                'class' => $sPhase,
                 'hint' => sprintf(t('rollback-hint'), $sPhase, $sVersion),
                 'label' => t('rollback')
-            ),
-            'setup' => array('class' => $sPhase,
+            ],
+            'setup' => [
+                'class' => 'btn',
                 'hint' => sprintf(t('setup-hint'), $sPhase, $sVersion),
                 'label' => t('setup'),
-                'class' => 'btn'
-            ),
-        );
+            ],
+        ];
         /*
           if (!$this->oUser->hasRole("project-action-$sFunction")){
           // $sClass .= ' disabled';
@@ -372,12 +400,11 @@ class projectgui extends project {
         $sRole = '';
         $sOnMouseover = '';
         $sOnMouseout = '';
-        switch($sFunction){
+        switch ($sFunction) {
             case 'accept';
                 $sRole = 'developer';
                 if ($sNext == "live") {
                     $sRole = 'pl';
-                    // $aLinkdata[$sFunction]['icon']='glyphicon glyphicon-star';
                 }
                 $sOnMouseover = '$(\'.td-phase-' . $sNext . '.td' . $this->_aConfig["id"] . '\').addClass(\'highlight\');';
                 $sOnMouseout = '$(\'.td-phase-' . $sNext . '.td' . $this->_aConfig["id"] . '\').removeClass(\'highlight\');';
@@ -390,11 +417,11 @@ class projectgui extends project {
             case 'deploy';
                 $sRole = 'developer';
                 $sOnMouseover = '$(\'.td-phase-' . $sPhase . '.td-place-ready2install.td' . $this->_aConfig["id"] . '\').addClass(\'highlight\');'
-                        .'$(\'.td-phase-' . $sPhase . '.td-place-deployed.td' . $this->_aConfig["id"] . '\').addClass(\'highlight\');'
-                        ;
+                    . '$(\'.td-phase-' . $sPhase . '.td-place-deployed.td' . $this->_aConfig["id"] . '\').addClass(\'highlight\');'
+                ;
                 $sOnMouseout = '$(\'.td-phase-' . $sPhase . '.td-place-ready2install.td' . $this->_aConfig["id"] . '\').removeClass(\'highlight\');'
-                        .'$(\'.td-phase-' . $sPhase . '.td-place-deployed.td' . $this->_aConfig["id"] . '\').removeClass(\'highlight\');'
-                        ;
+                    . '$(\'.td-phase-' . $sPhase . '.td-place-deployed.td' . $this->_aConfig["id"] . '\').removeClass(\'highlight\');'
+                ;
                 break;
         }
 
@@ -409,13 +436,13 @@ class projectgui extends project {
 
         $sLink = "/deployment/" . ($this->_aConfig["id"] ? $this->_aConfig["id"] : 'all/setup') . "/";
         if ($sFunction != "overview") {
-            $sLink.="$sFunction/";
+            $sLink .= "$sFunction/";
         }
         if ($sPhase) {
-            $sLink.="$sPhase/";
+            $sLink .= "$sPhase/";
         }
         if ($sVersion) {
-            $sLink.="$sVersion/";
+            $sLink .= "$sVersion/";
         }
         if (!$this->oUser->hasPermission("project-action-$sFunction")) {
             // $sClass .= ' disabled';
@@ -423,46 +450,47 @@ class projectgui extends project {
         }
 
         // $sClass='btn ' . (strstr('btn-', $sClass) ? '': 'btn-default ') .$sClass;
-        return $this->_oHtml->getLinkButton(array(
-                    'href' => $sLink,
-                    'title' => $sHint,
-                    'class' => $sClass,
-                    'type' => $sFunction,
-                    'onmouseover' => $sOnMouseover,
-                    'onmouseout' => $sOnMouseout,
-                    'label' => $sLabel,
-        ));
+        return $this->_oHtml->getLinkButton([
+            'href' => $sLink,
+            'title' => $sHint,
+            'class' => $sClass,
+            'type' => $sFunction,
+            'onmouseover' => $sOnMouseover,
+            'onmouseout' => $sOnMouseout,
+            'label' => $sLabel,
+        ]);
         // return '<a href="' . $sLink . '" ' . $sOnMouseover . ' title="' . $sHint . '" class="btn  btn-default ' . $sClass . '"><i class="' . $sIconClass . '"></i> ' . $sLabel . '</a>';
     }
 
     /**
-     * return html code for the setup form for a new project
+     * Get html code for the setup form for a new project
      * @return string
      */
-    public function renderNewProject() {
+    public function renderNewProject(): string
+    {
         global $aParams;
         if (!$this->oUser->hasPermission("project-action-create")) {
             return $this->oUser->showDenied();
         }
 
-        require_once ("formgen.class.php");
+        require_once("formgen.class.php");
         $i = 0;
         $sID = array_key_exists("id", $aParams) ? $aParams["id"] : "";
 
-        $aForms = array(
-            'setup' => array(
-                'meta' => array(
+        $aForms = [
+            'setup' => [
+                'meta' => [
                     'method' => 'POST',
                     'action' => '?',
-                ),
-                'validate' => array(),
-                'form' => array(
-                    'input' . $i++ => array(
+                ],
+                'validate' => [],
+                'form' => [
+                    'input' . $i++ => [
                         'type' => 'hidden',
                         'name' => 'setupaction',
                         'value' => 'create',
-                    ),
-                    'input' . $i++ => array(
+                    ],
+                    'input' . $i++ => [
                         'type' => 'text',
                         'name' => 'id',
                         'label' => t("class-project-info-setup-projectId"),
@@ -472,29 +500,36 @@ class projectgui extends project {
                         'size' => 100,
                         'pattern' => '[a-z0-9\-_]*',
                         'placeholder' => t("class-project-info-setup-projectId-placeholder"),
-                    ),
-                ),
-            ),
-        );
-        $aForms["setup"]["form"]['input' . $i++] = array(
+                    ],
+                ],
+            ],
+        ];
+        $aForms["setup"]["form"]['input' . $i++] = [
             'type' => 'submit',
             'name' => 'btnsave',
             'label' => t("save"),
             'value' => $this->_oHtml->getIcon('sign-ok') . t("save"),
-        );
+        ];
 
         $oForm = new formgen($aForms);
         return $oForm->renderHtml("setup");
     }
+
     /**
-     * render html for a place of a phase
+     * Get html for a place of a phase.
+     * It returns false when 
+     * - phase or place are empty 
+     * - phase is not active
+     * - place is not valid
+     * 
      * @param string  $sPhase    phase
      * @param string  $sPlace    name of the place; one of onhold|ready2install|deployed
      * @param bool    $bActions  draw action links (deploy, accept) on/ off
      * @param bool    $bLong     use long variant to display infos? 
      * @return string|boolean
      */
-    public function renderPhaseDetail($sPhase, $sPlace, $bActions = true, $bLong = true) {
+    public function renderPhaseDetail(string $sPhase, string $sPlace, bool $bActions = true, bool $bLong = true): bool|string
+    {
 
         if (!$sPhase) {
             return false;
@@ -508,7 +543,7 @@ class projectgui extends project {
         if (!array_key_exists($sPlace, $this->_aPlaces)) {
             return false;
         }
-  
+
         $sReturn = '';
         $aDataPhase = $this->getPhaseInfos($sPhase);
         $aData = $aDataPhase[$sPlace];
@@ -532,37 +567,36 @@ class projectgui extends project {
             if ($bLong) {
                 // long display of the revision
                 // $sJsonUrl = $this->_getInfofile($sPhase, $sPlace);
-                    $sReturn .=$this->_getChecksumDiv(
-                        $aData["revision"], 
-                        $this->_oHtml->getIconByType('calendar') .' ' . date($sDateFormat, $oPkgDate) . '<br>'
-                        . $this->_oHtml->getIconByType('branch') . t('branch') . ': ' . $aData["branch"] . '<br>'
-                        . $this->_oHtml->getIconByType('revision') . t('revision') . ': ' . $this->_renderRevision($aData["revision"]) . '<br>'
-                        . $this->_oHtml->getIconByType('comment') . t('commitmessage') . ':<br>'
-                    )
+                $sReturn .= $this->_getChecksumDiv(
+                    $aData["revision"],
+                    $this->_oHtml->getIconByType('calendar') . ' ' . date($sDateFormat, $oPkgDate) . '<br>'
+                    . $this->_oHtml->getIconByType('branch') . t('branch') . ': ' . $aData["branch"] . '<br>'
+                    . $this->_oHtml->getIconByType('revision') . t('revision') . ': ' . $this->_renderRevision($aData["revision"]) . '<br>'
+                    . $this->_oHtml->getIconByType('comment') . t('commitmessage') . ':<br>'
+                )
                     . '<pre>' . $this->transformCommitMessage($aData["message"]) . '</pre>'
-                // . '<i class="glyphicon glyphicon-globe"></i> ' . t('url') . ': <a href="' . $sJsonUrl . '">' . $sJsonUrl . '</a><br>'
                 ;
                 if ($sPlace == "deployed" && array_key_exists("url", $this->_aPrjConfig["phases"][$sPhase])) {
                     $sUrl = $this->_aPrjConfig["phases"][$sPhase]["url"];
-                    $sReturn.=$this->_oHtml->getIconByType('link-extern') . ' '. t('url') . ': <a href="' . $sUrl . '">' . $sUrl . '</a><br>';
+                    $sReturn .= $this->_oHtml->getIconByType('link-extern') . ' ' . t('url') . ': <a href="' . $sUrl . '">' . $sUrl . '</a><br>';
                 }
             } else {
                 $sReturn .= $this->_getChecksumDiv(
-                            $aData["revision"], 
-                            $this->_oHtml->getIconByType('calendar') .' ' . date($sDateFormat, $oPkgDate)
-                    );
+                    $aData["revision"],
+                    $this->_oHtml->getIconByType('calendar') . ' ' . date($sDateFormat, $oPkgDate)
+                );
                 if ($sPlace == "deployed" && array_key_exists("url", $this->_aPrjConfig["phases"][$sPhase])) {
-                    $sMore = $this->_oHtml->getIconByType('link-extern').' '
-                            . t('url')
-                            . ': <a href="' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '">' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '</a><br>';
+                    $sMore = $this->_oHtml->getIconByType('link-extern') . ' '
+                        . t('url')
+                        . ': <a href="' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '">' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '</a><br>';
                 }
 
-                $sReturn.=' ' . $this->renderInfoLink(
-                                $aData, 
-                                [
-                                    'title' => $this->getLabel() . " :: $sPhase :: $sPlace",
-                                    'more' => $sMore,
-                                ]
+                $sReturn .= ' ' . $this->renderInfoLink(
+                    $aData,
+                    [
+                        'title' => $this->getLabel() . " :: $sPhase :: $sPlace",
+                        'more' => $sMore,
+                    ]
                 );
             }
 
@@ -571,9 +605,9 @@ class projectgui extends project {
                     if (array_key_exists("phases", $this->_aConfig) && array_key_exists($sPhase, $this->_aConfig["phases"])) {
                         // $sReturn .= print_r($this->_aConfig["phases"][$sPhase], true);
                         if (count($this->_getDeploytimes($sPhase))) {
-                            $sReturn .= '<br>'.$this->_oHtml->getIcon('time').t('deploytimes') . ':<br>'
-                                    . implode("<br>", array_values($this->_getDeploytimes($sPhase)))
-                                    . '<br>';
+                            $sReturn .= '<br>' . $this->_oHtml->getIcon('time') . t('deploytimes') . ':<br>'
+                                . implode("<br>", array_values($this->_getDeploytimes($sPhase)))
+                                . '<br>';
                         }
                         if ($bActions) {
                             $sReturn .= ' ' . $this->renderLink("deploy", $sPhase);
@@ -601,13 +635,13 @@ class projectgui extends project {
             // $this->_getChecksumDiv($aData["revision"])
         } else {
             if (array_key_exists("error", $aData)) {
-                $sReturn.=''
-                        . $this->renderInfoLink(array('error' => $aData["error"]), array())
+                $sReturn .= ''
+                    . $this->renderInfoLink(['error' => $aData["error"], [] ])
                 ;
             } else if (array_key_exists("warning", $aData)) {
-                $sReturn.= '<div class="warning">'.$this->_oHtml->getIcon('sign-info'). t('warning') . ':<br>' . $aData["warning"] . '</div>';
+                $sReturn .= '<div class="warning">' . $this->_oHtml->getIcon('sign-info') . t('warning') . ':<br>' . $aData["warning"] . '</div>';
             } else {
-                
+
                 // OK = 1 ... for the queue we show no hint
                 return '';
                 /*
@@ -618,43 +652,44 @@ class projectgui extends project {
                 */
             }
         } // if
-        // } // for
         return $sReturn;
     }
 
     /**
-     * render html for the project overview; it shows the defined phases for 
+     * Get html for the project overview; it shows the defined phases for 
      * the project as a table
-     * @return type
+     * @return string
      */
-    public function renderPhaseInfo() {
+    public function renderPhaseInfo(): string
+    {
         $sRow1 = false;
         $sRow2 = false;
 
-        $renderAdminLTE=new renderadminlte();                    
+        $renderAdminLTE = new renderadminlte();
 
-        $iWidth=min(12 / count($this->getActivePhases()), 4);
+        $iWidth = min(12 / count($this->getActivePhases()), 4);
         foreach ($this->getActivePhases() as $sPhase) {
-            $sRow1.=$renderAdminLTE->addCol(
+            $sRow1 .= $renderAdminLTE->addCol(
                 '<table class="nomargin"><tr><th class="' . $sPhase . ' tdphase">' . $sPhase . '</th></tr></table>'
                 ,
-                $iWidth);
-              
-            $sDetails=t('url') . ': <a href="' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '">' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '</a><br>'
-            . '<br>' . t('deploytimes') . ':<br>';
+                $iWidth
+            );
+
+            $sDetails = t('url') . ': <a href="' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '">' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '</a><br>'
+                . '<br>' . t('deploytimes') . ':<br>';
             if (count($this->_getDeploytimes($sPhase))) {
-                $sDetails.=implode("<br>", $this->_getDeploytimes($sPhase));
+                $sDetails .= implode("<br>", $this->_getDeploytimes($sPhase));
             } else {
-                $sDetails.=t('deploytimes-immediately');
+                $sDetails .= t('deploytimes-immediately');
             }
-            $sDetails.='<br>' . $this->renderLink("phase", $sPhase)
-                    . $this->_renderHosts($sPhase)
-                    . '<br>'
-                    . $this->_renderFiles($sPhase)
-                    ;
+            $sDetails .= '<br>' . $this->renderLink("phase", $sPhase)
+                . $this->_renderHosts($sPhase)
+                . '<br>'
+                . $this->_renderFiles($sPhase)
+            ;
 
-            $sRow2.=$renderAdminLTE->addCol(
-                $renderAdminLTE->getCard(array (
+            $sRow2 .= $renderAdminLTE->addCol(
+                $renderAdminLTE->getCard([
                     'class' => $sPhase,
                     'variant' => '',
                     'tb-remove' => 1,
@@ -663,106 +698,107 @@ class projectgui extends project {
                     'tools' => '',
                     'text' => $sDetails,
                     'footer' => '',
-                )), 
+                ]),
                 $iWidth
             );
         }
         return ''
-            .$renderAdminLTE->addRow($sRow1)
-            .$renderAdminLTE->addRow($sRow2)
-            ;
+            . $renderAdminLTE->addRow($sRow1)
+            . $renderAdminLTE->addRow($sRow2)
+        ;
 
     }
 
     /**
-     * render html for a row with td for all places (first row)
+     * Get html for a row with td for all places (first row)
      * @param string $sPhase  phase (just needed for coloring)
      * @return string
      */
-    public function renderPlacesAsTd($sPhase) {
+    public function renderPlacesAsTd(string $sPhase): string
+    {
         $sRow1 = '';
         foreach (array_keys($this->_aPlaces) as $sPlace) {
-            $sRow1.='<td class="' . $sPhase . ' ' . $this->_aConfig["id"] . ' tdphase">' . t($sPlace) . '</td>';
+            $sRow1 .= '<td class="' . $sPhase . ' ' . $this->_aConfig["id"] . ' tdphase">' . t($sPlace) . '</td>';
         }
         return $sRow1;
     }
 
     /**
-     * return html code for the setup form of an exsiting project
+     * Get html code for the setup form of an exsiting project
      * @return string
      */
-    public function renderProjectSetup() {
+    public function renderProjectSetup(): string
+    {
         if (!$this->oUser->hasPermission("project-action-setup")) {
             return $this->oUser->showDenied();
         }
         $sMessages = '';
-        require_once ("formgen.class.php");
+        require_once("formgen.class.php");
 
+        $aSelectProjectGroup = [
+            'type' => 'select',
+            'name' => 'projectgroup',
+            'label' => t("projectgroup"),
+            'options' => [
+                OPTION_NONE => [
+                    'label' => t('none'),
+                ],
+                '' => [
+                    'label' => '- - - - - - - - - - - - - - - - - - - - ',
+                ],
+            ],
+        ];
+        foreach ($this->_aConfig['projectgroups'] as $sGroupid => $sGroupLabel) {
+            $bActive = $this->getProjectGroup() === $sGroupid;
+            $aSelectProjectGroup['options'][$sGroupid] = [
+                'label' => $sGroupLabel,
+                'selected' => $bActive ? 'selected' : false,
+            ];
+        }
 
-            $aSelectProjectGroup = array(
-                'type' => 'select',
-                'name' => 'projectgroup',
-                'label' => t("projectgroup"),
-                'options' => array(
-                    OPTION_NONE => array(
-                        'label' => t('none'),
-                    ),
-                    '' => array(
-                        'label' => '- - - - - - - - - - - - - - - - - - - - ',
-                    ),
-                ),
-            );
-            foreach($this->_aConfig['projectgroups'] as $sGroupid=>$sGroupLabel){
-                $bActive=$this->getProjectGroup() === $sGroupid;
-                $aSelectProjectGroup['options'][$sGroupid] = array(
-                    'label' => $sGroupLabel,
-                    'selected' => $bActive ? 'selected' : false,
-                );
-            }
-            
-        $aSelectSlack = array(
-                'type' => 'hidden',
-                'name' => 'messenger[slack]',
-                'value' => false,
-        );
+        $aSelectSlack = [
+            'type' => 'hidden',
+            'name' => 'messenger[slack]',
+            'value' => false,
+        ];
         if (
-                isset($this->_aConfig['messenger']['slack']['presets'])
-                && count($this->_aConfig['messenger']['slack']['presets'])
+            isset($this->_aConfig['messenger']['slack']['presets'])
+            && count($this->_aConfig['messenger']['slack']['presets'])
         ) {
-            $aSelectSlack = array(
+            $aSelectSlack = [
                 'type' => 'select',
                 'name' => 'messenger[slack]',
                 'label' => t("messenger-slack"),
-                'options' => array(
-                    OPTION_NONE => array(
+                'options' => [
+                    OPTION_NONE => [
                         'label' => t('none'),
-                    ),
-                    '' => array(
+                    ],
+                    '' => [
                         'label' => '- - - - - - - - - - - - - - - - - - - - ',
-                    ),
-                ),
-            );
-            foreach($this->_aConfig['messenger']['slack']['presets'] as $sSlackUrl=>$aSlackCfg){
-                $bActive=$this->_aPrjConfig['messenger']['slack'] === $sSlackUrl;
-                $aSelectSlack['options'][$sSlackUrl] = array(
+                    ],
+                ],
+            ];
+            foreach ($this->_aConfig['messenger']['slack']['presets'] as $sSlackUrl => $aSlackCfg) {
+                $bActive = $this->_aPrjConfig['messenger']['slack'] === $sSlackUrl;
+                $aSelectSlack['options'][$sSlackUrl] = [
                     'label' => array_key_exists('label', $aSlackCfg) ? $aSlackCfg['label'] : $sSlackUrl,
                     'selected' => $bActive ? 'selected' : false,
-                );
+                ];
             }
-            
+
         }
         // ---------- Build plugins
         /*
         
-        $aPluginsBuild = array(
-            'select' => array(
+        $aPluginsBuild = [
+            'select' => [
                 'type' => 'checkbox',
                 'name' => 'build[enabled_build_plugins]',
                 'label' => t("build-plugins"),
                 'options' => [],
-            ),
+            ],
             // 'project-config' => '',
-        );
+        ];
         foreach (array_keys($this->getConfiguredPlugins('build')) as $sPluginName){
 
             $sPluginFile=$this->_getPluginFilename('build', $sPluginName);
@@ -773,20 +809,20 @@ class projectgui extends project {
                 include_once $this->_getPluginFilename('build', $sPluginName);
                 $TmpRolloutPlugin = new $sMyClassname([]);
                 echo "FOUND $sMyClassname<br>";
-                $aPluginsBuild['select']['options'][$sPluginName]=array(
+                $aPluginsBuild['select']['options'][$sPluginName]=[
                         'label' => $TmpRolloutPlugin->getName(),
                         'checked' => $bActive,
                         // 'onclick' => '$(\'.'.$sMyDivClass.'\').hide(); $(\'.' . $sMyDivClassActive . '\').show();',
-                    );
+                ];
                 } catch (Exception $ex) {
 
                 }
             } else {
-                $aRollout['project-select']['options'][$sPluginName]=array(
+                $aRollout['project-select']['options'][$sPluginName]=[
                         'label' => 'not found: <span class="error">' . $sMyClassname . '</span>',
                         'checked' => false,
                         'disabled' => "disabled",
-                    );
+                ];
 
                 
             }
@@ -795,74 +831,74 @@ class projectgui extends project {
         */
 
         // ---------- /Build plugins
-        
+
         // ---------- Rollout plugins
-        $aRollout = array(
-            'project-select' => array(
+        $aRollout = [
+            'project-select' => [
                 'type' => 'radio',
                 'name' => 'deploy[enabled_rollout_plugin]',
                 'label' => t("deploy-rollout-plugin"),
-            ),
+            ],
             'project-config' => '',
-        );
-        foreach (array_keys($this->getConfiguredPlugins('rollout')) as $sPluginName){
+        ];
+        foreach (array_keys($this->getConfiguredPlugins('rollout')) as $sPluginName) {
 
-            $sPluginFile=$this->_getPluginFilename('rollout', $sPluginName);
+            $sPluginFile = $this->_getPluginFilename('rollout', $sPluginName);
             $TmpRolloutPlugin = false;
-            $sMyClassname='rollout_'. $sPluginName;
-            $sMyDivId='rollout-'. $sPluginName.'-config';
-            $sMyDivClass='rolloutconfigdiv';
-            $sMyDivClassActive='rolloutconfigdiv-'. $sPluginName;
-            $bActive=$sPluginName === $this->oRolloutPlugin->getId();
-
-            if(file_exists($sPluginFile)){
-                try{
+            $sMyClassname = 'rollout_' . $sPluginName;
+            $sMyDivId = 'rollout-' . $sPluginName . '-config';
+            $sMyDivClass = 'rolloutconfigdiv';
+            $sMyDivClassActive = 'rolloutconfigdiv-' . $sPluginName;
+            $bActive = $sPluginName === $this->oRolloutPlugin->getId();
+
+            if (file_exists($sPluginFile)) {
+                try {
                     include_once $this->_getPluginFilename('rollout', $sPluginName);
-                    $TmpRolloutPlugin = new $sMyClassname(array(
-                        'lang'=>$this->_aConfig['lang'],
-                        'phase'=>false,
-                        'globalcfg'=>$this->_aConfig['plugins']['rollout'][$sPluginName],
-                        'projectcfg'=>$this->_aPrjConfig,
-                    ));
-                    $aRollout['project-select']['options'][$sPluginName]=array(
-                            'label' => $TmpRolloutPlugin->getName(),
-                            'checked' => $bActive,
-                            'onclick' => '$(\'.'.$sMyDivClass.'\').hide(); $(\'.' . $sMyDivClassActive . '\').show();',
-                        );
-                    
-                    $aRollout['project-config'].=''
-                            . '<div id="'.$sMyDivId.'" class="'.$sMyDivClass.' '.$sMyDivClassActive.'"'
-                            . ($bActive ? '' : ' style="display: none;"' )
-                            . '>'
-                                . $TmpRolloutPlugin->renderFormdata4Project()
-                            . '</div>'
-                            ;
-                    
+                    $TmpRolloutPlugin = new $sMyClassname([
+                        'lang' => $this->_aConfig['lang'],
+                        'phase' => false,
+                        'globalcfg' => $this->_aConfig['plugins']['rollout'][$sPluginName],
+                        'projectcfg' => $this->_aPrjConfig,
+                    ]);
+                    $aRollout['project-select']['options'][$sPluginName] = [
+                        'label' => $TmpRolloutPlugin->getName(),
+                        'checked' => $bActive,
+                        'onclick' => '$(\'.' . $sMyDivClass . '\').hide(); $(\'.' . $sMyDivClassActive . '\').show();',
+                    ];
+
+                    $aRollout['project-config'] .= ''
+                        . '<div id="' . $sMyDivId . '" class="' . $sMyDivClass . ' ' . $sMyDivClassActive . '"'
+                        . ($bActive ? '' : ' style="display: none;"')
+                        . '>'
+                        . $TmpRolloutPlugin->renderFormdata4Project()
+                        . '</div>'
+                    ;
+
                     // generate form firlds for each phase
-                    foreach(array_keys($this->getPhases()) as $sMyPhase){
-                        $aRollout[$sMyPhase].=''
-                            . '<div id="'.$sMyDivId.'-'.$sMyPhase.'" class="'.$sMyDivClass.' '.$sMyDivClassActive.'"'
-                            . ($bActive ? '' : ' style="display: none;"' )
+                    foreach (array_keys($this->getPhases()) as $sMyPhase) {
+                        $aRollout[$sMyPhase] .= ''
+                            . '<div id="' . $sMyDivId . '-' . $sMyPhase . '" class="' . $sMyDivClass . ' ' . $sMyDivClassActive . '"'
+                            . ($bActive ? '' : ' style="display: none;"')
                             . '>'
-                                . $TmpRolloutPlugin->renderFormdata4Phase($sMyPhase)
+                            . $TmpRolloutPlugin->renderFormdata4Phase($sMyPhase)
                             . '</div>'
-                            ;
+                        ;
                     }
                 } catch (Exception $ex) {
 
                 }
             } else {
-                $aRollout['project-select']['options'][$sPluginName]=array(
-                        'label' => 'not found: <span class="error">' . $sMyClassname . '</span>',
-                        'checked' => false,
-                        'disabled' => "disabled",
-                    );
+                $aRollout['project-select']['options'][$sPluginName] = [
+                    'label' => 'not found: <span class="error">' . $sMyClassname . '</span>',
+                    'checked' => false,
+                    'disabled' => "disabled",
+                ];
+
 
-                
             }
         }
         // ---------- /Rollout plugins
-        
+
         $aForemanHostgroups = false;
         $iForemanHostgroupDefault = false;
         $sForemanHostgroupDefault = false;
@@ -874,35 +910,36 @@ class projectgui extends project {
             // $oForeman->setDebug(1);
             // $oForeman->selfcheck(); die(__FUNCTION__);
 
-            $aForemanHostgroups = $oForeman->read(array(
-                'request' => array(
-                    array('hostgroups'),
-                // array('operatingsystems',4),
-                ),
-                'response' => array(
-                    'id', 'title'
-                ),
-            ));
-            $aSelectForemanGroups = array(
+            $aForemanHostgroups = $oForeman->read([
+                'request' => [
+                    ['hostgroups'],
+                    // ['operatingsystems',4],
+                ],
+                'response' => [
+                    'id',
+                    'title'
+                ],
+            ]);
+            $aSelectForemanGroups = [
                 'type' => 'select',
                 'name' => 'deploy[foreman][hostgroup]',
                 'label' => $this->_oHtml->getIcon('foreman') . t("foreman-hostgroup"),
-                'options' => array(
-                    OPTION_NONE => array(
+                'options' => [
+                    OPTION_NONE => [
                         'label' => t('none'),
-                    ),
-                    '' => array(
+                    ],
+                    '' => [
                         'label' => '- - - - - - - - - - - - - - - - - - - - ',
-                    ),
-                ),
-            );
+                    ],
+                ],
+            ];
             if ($aForemanHostgroups && count($aForemanHostgroups)) {
                 foreach ($aForemanHostgroups as $aItem) {
-                    $bActive=$iForemanHostgroupDefault === (int) $aItem['id'];
-                    $aSelectForemanGroups['options'][$aItem['id']] = array(
+                    $bActive = $iForemanHostgroupDefault === (int) $aItem['id'];
+                    $aSelectForemanGroups['options'][$aItem['id']] = [
                         'label' => $aItem['title'],
                         'selected' => $bActive ? 'selected' : false,
-                    );
+                    ];
                     $sForemanHostgroupDefault = $bActive ? $aItem['title'] : $sForemanHostgroupDefault;
                 }
             }
@@ -912,87 +949,87 @@ class projectgui extends project {
         $i = 0;
 
         $aPrefixItem = count($this->getVersions()) ?
-                array(
-            'type' => 'markup',
-            'value' => '<div class="form-group">
+            [
+                'type' => 'markup',
+                'value' => '<div class="form-group">
                         <label class="col-sm-2">' . t('fileprefix') . '</label>
                         <div class="col-sm-10">
                             <input id="inputprefix" type="hidden" name="fileprefix" value="' . $this->_aPrjConfig["fileprefix"] . '">
                             ' . $this->_aPrjConfig["fileprefix"] . '
                         </div></div>
                             ',
-                ) : array(
-            'type' => 'text',
-            'name' => 'fileprefix',
-            // 'disabled' => 'disabled',
-            'label' => t('fileprefix-label'),
-            'value' => $this->_aPrjConfig["fileprefix"],
-            'required' => 'required',
-            'validate' => 'isastring',
-            'pattern' => '[a-z0-9\-_]*',
-            'size' => 100,
-            'placeholder' => '',
-        );
+            ] : [
+                'type' => 'text',
+                'name' => 'fileprefix',
+                // 'disabled' => 'disabled',
+                'label' => t('fileprefix-label'),
+                'value' => $this->_aPrjConfig["fileprefix"],
+                'required' => 'required',
+                'validate' => 'isastring',
+                'pattern' => '[a-z0-9\-_]*',
+                'size' => 100,
+                'placeholder' => '',
+            ];
 
         // detect access to repo url
-        $aBranches=$this->getRemoteBranches(true);
+        $aBranches = $this->getRemoteBranches(true);
         // $aRepodata = $this->getRepoRevision();
 
         // if (is_array($aRepodata) && array_key_exists("message", $aRepodata)) {
         if (is_array($aBranches) && count($aBranches)) {
             $sRepoCheck = '<span class="ok">' . sprintf(t('class-project-info-repoaccess'), count($aBranches)) . '</span>';
         } else {
-            $sRepoError=sprintf(t('class-project-error-no-repoaccess'), $aRepodata["error"]);
+            $sRepoError = sprintf(t('class-project-error-no-repoaccess'), $aRepodata["error"]);
             $sRepoCheck = '<span class="error">' . $sRepoError . '</span>';
-            $sMessages.=$this->_oHtml->getBox("error", $sRepoError);
+            $sMessages .= $this->_oHtml->getBox("error", $sRepoError);
         }
 
         // generate datalist with exisating ssh keys for auth field
         $sAuthListitems = '';
         foreach ($this->_getSshKeys() as $sKey) {
-            $sAuthListitems.='<option value="' . $sKey . '">';
+            $sAuthListitems .= '<option value="' . $sKey . '">';
         }
-        $aForms = array(
-            'setup' => array(
-                'meta' => array(
+        $aForms = [
+            'setup' => [
+                'meta' => [
                     'method' => 'POST',
                     'action' => '?',
-                ),
-                'validate' => array(),
-                'form' => array(
-                    'input' . $i++ => array(
+                ],
+                'validate' => [],
+                'form' => [
+                    'input' . $i++ => [
                         'type' => 'hidden',
                         'name' => 'setupaction',
                         'value' => 'save',
-                    ),
-                    'input' . $i++ => array(
+                    ],
+                    'input' . $i++ => [
                         'type' => 'hidden',
                         'name' => 'id',
                         'value' => $this->_aConfig["id"],
-                    ),
-                    'input' . $i++ => array(
+                    ],
+                    'input' . $i++ => [
                         'type' => 'markup',
                         'value' => '<div class="tabbable">
                             <ul class="nav nav-tabs">
-                                <li class="active"><a href="#tab1" class="nav-link active" data-toggle="tab" aria-selected="1">' . $this->_oHtml->getIcon('list').t('setup-metadata') . '</a></li>
-                                <li><a href="#tab2" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('repository').t('repositoryinfos') . '</a></li>
+                                <li class="active"><a href="#tab1" class="nav-link active" data-toggle="tab" aria-selected="1">' . $this->_oHtml->getIcon('list') . t('setup-metadata') . '</a></li>
+                                <li><a href="#tab2" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('repository') . t('repositoryinfos') . '</a></li>
 
-                                <li><a href="#tab3" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('deploy-configfile').t('deploy-configfile') . '</a></li>
-                                <li><a href="#tab4" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('deploy-rollout-plugin').t('deploy-rollout-plugin') . '</a></li>
-                                <li><a href="#tab5" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('phase').t('phases') . '</a></li>
-                                <li><a href="#tab6" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('raw-data').t('raw-data') . '</a></li>
+                                <li><a href="#tab3" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('deploy-configfile') . t('deploy-configfile') . '</a></li>
+                                <li><a href="#tab4" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('deploy-rollout-plugin') . t('deploy-rollout-plugin') . '</a></li>
+                                <li><a href="#tab5" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('phase') . t('phases') . '</a></li>
+                                <li><a href="#tab6" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('raw-data') . t('raw-data') . '</a></li>
                             </ul>
                             <div class="tab-content">
                             <div class="tab-pane fade active show" id="tab1">
                             <br>
                             
                             ',
-                    ),
+                    ],
 
                     // --------------------------------------------------
                     // Tab for metadata
                     // -------------------------------------------------
-                    'input' . $i++ => array(
+                    'input' . $i++ => [
                         'type' => 'text',
                         'name' => 'label',
                         'label' => t('projectname'),
@@ -1001,8 +1038,8 @@ class projectgui extends project {
                         'validate' => 'isastring',
                         'size' => 100,
                         'placeholder' => 'Projekt',
-                    ),
-                    'input' . $i++ => array(
+                    ],
+                    'input' . $i++ => [
                         'type' => 'text',
                         'name' => 'description',
                         'label' => t('projectdescription'),
@@ -1011,8 +1048,8 @@ class projectgui extends project {
                         'validate' => 'isastring',
                         'size' => 100,
                         'placeholder' => '',
-                    ),
-                    'input' . $i++ => array(
+                    ],
+                    'input' . $i++ => [
                         'type' => 'text',
                         'name' => 'contact',
                         'label' => t('contact'),
@@ -1021,15 +1058,15 @@ class projectgui extends project {
                         'validate' => 'isastring',
                         'size' => 100,
                         'placeholder' => '',
-                    ),
+                    ],
 
                     'input' . $i++ => $aSelectProjectGroup,
 
-                    'input' . $i++ => array(
+                    'input' . $i++ => [
                         'type' => 'markup',
                         'value' => '<p>' . t('messenger') . '</p>',
-                    ),
-                    'input' . $i++ => array(
+                    ],
+                    'input' . $i++ => [
                         'type' => 'text',
                         'name' => 'messenger[email]',
                         'label' => t("messenger-email"),
@@ -1038,19 +1075,19 @@ class projectgui extends project {
                         'size' => 100,
                         'placeholder' => '',
                         'autocomplete' => 'off',
-                    ),
-                    
+                    ],
+
                     'input' . $i++ => $aSelectSlack,
-                    
+
                     // --------------------------------------------------
                     // Tab soources repository & build
                     // --------------------------------------------------
-                    'input' . $i++ => array(
+                    'input' . $i++ => [
                         'type' => 'markup',
                         'value' => ' </div><div class="tab-pane fade" id="tab2">
                             <p>' . t('setup-hint-build') . '</p>',
-                    ),
-                    'input' . $i++ => array(
+                    ],
+                    'input' . $i++ => [
                         'type' => 'text',
                         'name' => 'build[type]',
                         'label' => t("build-type"),
@@ -1059,8 +1096,8 @@ class projectgui extends project {
                         'validate' => 'isastring',
                         'size' => 100,
                         'placeholder' => '',
-                    ),
-                    'input' . $i++ => array(
+                    ],
+                    'input' . $i++ => [
                         'type' => 'text',
                         'name' => 'build[url]',
                         'label' => t("repository-url"),
@@ -1069,8 +1106,8 @@ class projectgui extends project {
                         'validate' => 'isastring',
                         'size' => 100,
                         'placeholder' => '',
-                    ),
-                    'input' . $i++ => array(
+                    ],
+                    'input' . $i++ => [
                         'type' => 'text',
                         'name' => 'build[auth]',
                         'label' => t("repository-auth"),
@@ -1080,19 +1117,19 @@ class projectgui extends project {
                         'validate' => 'isastring',
                         'size' => 100,
                         'placeholder' => '',
-                    ),
-                    'input' . $i++ => array(
+                    ],
+                    'input' . $i++ => [
                         'type' => 'markup',
                         'value' => '<datalist id="listauth">' . $sAuthListitems . '</datalist>',
-                    ),
-                    'input' . $i++ => array(
+                    ],
+                    'input' . $i++ => [
                         'type' => 'markup',
                         'value' => '<div class="form-group">'
-                        . '<label class="col-sm-2"> </label><div class="col-sm-10">'
-                        . $sRepoCheck
-                        . '</div></div>',
-                    ),
-                    'input' . $i++ => array(
+                            . '<label class="col-sm-2"> </label><div class="col-sm-10">'
+                            . $sRepoCheck
+                            . '</div></div>',
+                    ],
+                    'input' . $i++ => [
                         'type' => 'text',
                         'name' => 'build[webaccess]',
                         'label' => t("repository-urlwebgui"),
@@ -1100,48 +1137,48 @@ class projectgui extends project {
                         'validate' => 'isastring',
                         'size' => 100,
                         'placeholder' => '',
-                    ),
+                    ],
                     'input' . $i++ => $aPrefixItem,
-                    'input' . $i++ => array(
+                    'input' . $i++ => [
                         'type' => 'markup',
                         'value' => '<div style="clear: both"></div>',
-                    ),
+                    ],
                     // task#1498 - handle project without "public" directory
-                    'input' . $i++ => array(
+                    'input' . $i++ => [
                         'type' => 'checkbox',
                         'name' => 'build[haspublic]',
                         'label' => t("repository-has-public-dir"),
                         'required' => false,
                         'validate' => 'isastring',
-                        'options' => array(
-                            '1' => array(
+                        'options' => [
+                            '1' => [
                                 'label' => t("yes"),
                                 'checked' => (array_key_exists('haspublic', $this->_aPrjConfig["build"]) ? $this->_aPrjConfig["build"]["haspublic"] : 0),
-                            ),
-                        ),
-                    ),
+                            ],
+                        ],
+                    ],
 
                     // --------------------------------------------------
                     // Tab for config and API key
                     // --------------------------------------------------
-                    'input' . $i++ => array(
+                    'input' . $i++ => [
                         'type' => 'markup',
                         'value' => ' </div><div class="tab-pane fade" id="tab3">
                             <p>' . t('deploy-configfile-hint') . '</p>',
-                    ),
-                    'textarea' . $i++ => array(
+                    ],
+                    'textarea' . $i++ => [
                         'type' => 'textarea',
                         'name' => 'deploy[configfile]',
                         'label' => t("deploy-configfile"),
                         'value' => $this->_aPrjConfig['deploy']["configfile"],
                         // 'required' => 'required',
-                        'validate' => 'isastring',  
+                        'validate' => 'isastring',
                         'cols' => 100,
                         'rows' => 10,
                         'placeholder' => 'export myvariable=&quot;hello world&quot;',
-                    ),
-   
-                    'input' . $i++ => array(
+                    ],
+
+                    'input' . $i++ => [
                         'type' => 'text',
                         'name' => 'api[secret]',
                         'label' => t("api-secret"),
@@ -1149,56 +1186,56 @@ class projectgui extends project {
                         'validate' => 'isastring',
                         'size' => 100,
                         'placeholder' => '',
-                    ),                    
-                    'input' . $i++ => array(
+                    ],
+                    'input' . $i++ => [
                         'type' => 'markup',
                         'value' => '<div class="col-sm-12">'
-                        . '<p>' . t('api-secret-hint') . '<br>'
-                            . '<a href="#" class="btn btn-default" onclick="$(\'#input'.($i-2).'\').val(generateSecret(64)); return false">'.t("api-secret-generate").'</a>'
-                        . '</p></div>',
-                    ),
-                    
+                            . '<p>' . t('api-secret-hint') . '<br>'
+                            . '<a href="#" class="btn btn-default" onclick="$(\'#input' . ($i - 2) . '\').val(generateSecret(64)); return false">' . t("api-secret-generate") . '</a>'
+                            . '</p></div>',
+                    ],
+
                     // --------------------------------------------------
                     // Tab rollout plugin
                     // --------------------------------------------------
-                    'input' . $i++ => array(
+                    'input' . $i++ => [
                         'type' => 'markup',
                         'value' => ' </div><div class="tab-pane fade" id="tab4">
                             <p>' . t('deploy-rollout-plugin-hint') . '</p>',
-                    ),
+                    ],
                     // select box for active rollout plugin
                     $aRollout['project-select'],
-                    
+
                     // project based config 
-                    'input' . $i++ => array(
+                    'input' . $i++ => [
                         'type' => 'markup',
                         'value' => ''
                             . '<hr>'
-                                .'<label class="col-sm-2">'.t('deploy-rollout-plugin-config') .'</label>'
-                                .'<div class="col-sm-10">'. $aRollout['project-config'].'</div>'
-                    ),
+                            . '<label class="col-sm-2">' . t('deploy-rollout-plugin-config') . '</label>'
+                            . '<div class="col-sm-10">' . $aRollout['project-config'] . '</div>'
+                    ],
                     // --------------------------------------------------
-                    'input' . $i++ => array(
+                    'input' . $i++ => [
                         'type' => 'markup',
                         'value' => ' </div><div class="tab-pane fade" id="tab5">
                             <p>' . sprintf(t("class-project-info-setup-phaseinfos"), $this->getNextPhase()) . '</p>',
-                    ),
-                ),
-            ),
-        );
+                    ],
+                ],
+            ],
+        ];
         // --------------------------------------------------
         // Tab for phases
         // --------------------------------------------------
         if ($aSelectForemanGroups) {
-            $aForms["setup"]["form"]['input' . $i++] = array(
+            $aForms["setup"]["form"]['input' . $i++] = [
                 'type' => 'markup',
-                'value' => '<strong>'.t("defaults-all-phases").'</strong><br><br>',
-            );
+                'value' => '<strong>' . t("defaults-all-phases") . '</strong><br><br>',
+            ];
             $aForms["setup"]["form"]['input' . $i++] = $aSelectForemanGroups;
-            $aForms["setup"]["form"]['input' . $i++] = array(
+            $aForms["setup"]["form"]['input' . $i++] = [
                 'type' => 'markup',
                 'value' => '<br><br>',
-            );
+            ];
         }
         foreach (array_keys($this->getPhases()) as $sPhase) {
 
@@ -1227,50 +1264,50 @@ class projectgui extends project {
 
             if ($aSelectForemanGroups) {
                 $iForemanHostgroup = (int) $this->_aPrjConfig['phases'][$sPhase]['foreman-hostgroup'];
-                $aSelectForemanHostGroup = array(
+                $aSelectForemanHostGroup = [
                     'type' => 'select',
                     'name' => 'phases[' . $sPhase . '][foreman-hostgroup]',
                     'label' => $this->_oHtml->getIcon('foreman') . t("foreman-hostgroup"),
-                    'options' => array(
-                        OPTION_DEFAULT => array(
+                    'options' => [
+                        OPTION_DEFAULT => [
                             'label' => t('default') . ' (' . $sForemanHostgroupDefault . ')',
                             'selected' => $iForemanHostgroup === OPTION_DEFAULT ? 'selected' : false,
-                        ),
-                        OPTION_NONE => array(
+                        ],
+                        OPTION_NONE => [
                             'label' => t('none'),
                             'selected' => $iForemanHostgroup === OPTION_NONE ? 'selected' : false,
-                        ),
-                        '' => array(
+                        ],
+                        '' => [
                             'label' => '- - - - - - - - - - - - - - - - - - - - ',
-                        ),
-                    ),
-                );
+                        ],
+                    ],
+                ];
                 if (is_array($aForemanHostgroups) && count($aForemanHostgroups)) {
                     foreach ($aForemanHostgroups as $aItem) {
-                        $aSelectForemanHostGroup['options'][$aItem['id']] = array(
+                        $aSelectForemanHostGroup['options'][$aItem['id']] = [
                             'label' => $aItem['title'],
                             'selected' => ($iForemanHostgroup === $aItem['id']) ? 'selected' : false,
-                        );
+                        ];
                     }
                 }
             }
-            $aForms["setup"]["form"]['input' . $i++] = array(
+            $aForms["setup"]["form"]['input' . $i++] = [
                 'type' => 'markup',
                 'value' => ''
-                // .'<pre>'.print_r($this->_aPrjConfig["phases"][$sPhase], 1).'</pre>'
-                /*
-                  . '<a class="'.$sPhase.'">'
-                  . t("phase") . ' ' . $sPhase
-                  . '</a>'
-                 */
-                . '<table class="table">'
-                . '<tbody>'
-                . '<tr><th class="' . $sPhase . '">' . $this->_oHtml->getIcon('phase') . t("phase") . ' ' . $sPhase . '</th></tr>'
-                . '<tr><td class="' . ($bActivePhase ? $sPhase : '') . '">'
-                . ''
-            );
+                    // .'<pre>'.print_r($this->_aPrjConfig["phases"][$sPhase], 1).'</pre>'
+                    /*
+                      . '<a class="'.$sPhase.'">'
+                      . t("phase") . ' ' . $sPhase
+                      . '</a>'
+                     */
+                    . '<table class="table">'
+                    . '<tbody>'
+                    . '<tr><th class="' . $sPhase . '">' . $this->_oHtml->getIcon('phase') . t("phase") . ' ' . $sPhase . '</th></tr>'
+                    . '<tr><td class="' . ($bActivePhase ? $sPhase : '') . '">'
+                    . ''
+            ];
 
-            $aForms["setup"]["form"]['input' . $i++] = array(
+            $aForms["setup"]["form"]['input' . $i++] = [
                 'type' => 'checkbox',
                 'name' => 'phases[' . $sPhase . '][active]',
                 'label' => t("phase-is-active"),
@@ -1279,20 +1316,20 @@ class projectgui extends project {
                 'validate' => 'isastring',
                 // 'size' => 100,
                 // 'placeholder' => '...',
-                'options' => array(
-                    '1' => array(
+                'options' => [
+                    '1' => [
                         'label' => t("yes"),
                         'checked' => $bActivePhase,
                         'onclick' => '$(\'#' . $sDivId4PhaseSettings . '\').css(\'display\', (this.checked ? \'block\' : \'none\') )',
-                    ),
-                ),
-            );
-            $aForms["setup"]["form"]['input' . $i++] = array(
+                    ],
+                ],
+            ];
+            $aForms["setup"]["form"]['input' . $i++] = [
                 'type' => 'markup',
                 'value' => ''
-                . '<div id="' . $sDivId4PhaseSettings . '" ' . ($bActivePhase ? '' : ' style="display: none;"') . '>'
-            );
-            $aForms["setup"]["form"]['input' . $i++] = array(
+                    . '<div id="' . $sDivId4PhaseSettings . '" ' . ($bActivePhase ? '' : ' style="display: none;"') . '>'
+            ];
+            $aForms["setup"]["form"]['input' . $i++] = [
                 'type' => 'text',
                 'name' => 'phases[' . $sPhase . '][url]',
                 'label' => $this->_oHtml->getIcon('url') . t("url-project-website"),
@@ -1301,8 +1338,8 @@ class projectgui extends project {
                 'validate' => 'isastring',
                 'size' => 100,
                 'placeholder' => 'https://' . $sPhase . '.[' . t("project") . '].[...]/',
-            );
-            $aForms["setup"]["form"]['input' . $i++] = array(
+            ];
+            $aForms["setup"]["form"]['input' . $i++] = [
                 'type' => 'radio',
                 'name' => 'phases[' . $sPhase . '][deploymethod]',
                 'label' => $this->_oHtml->getIcon('method') . t("deploymethod"),
@@ -1311,46 +1348,46 @@ class projectgui extends project {
                 'validate' => 'isastring',
                 // 'size' => 100,
                 // 'placeholder' => '...',
-                'options' => array(
-                    'none' => array(
+                'options' => [
+                    'none' => [
                         'label' => t("deploymethod-none"),
                         'checked' => $sDeploymethod === "none",
                         'onclick' => '$(\'#' . $sDivId4TargetHosts . '\').css(\'display\', (this.checked ? \'none\' : \'block\') )',
-                    ),
-                    'rolloutplugin' => array(
+                    ],
+                    'rolloutplugin' => [
                         // 'label' => t("deploymethod-puppet").' - '.  $this->oRolloutPlugin->getName(),
                         'label' => t("deploymethod-rolloutplugin"),
                         'checked' => $sDeploymethod === "rolloutplugin",
                         'onclick' => '$(\'#' . $sDivId4TargetHosts . '\').css(\'display\', (this.checked ? \'block\' : \'none\') )',
-                    ),
-                /*
-                 * see deploy method to handle an action
-                  'sshproxy' => array(
-                  'label' => t("deploymethod-sshproxy"),
-                  'checked' => $sDeploymethod==="sshproxy",
-                  'onclick' => '$(\'#'.$sDivId4TargetHosts.'\').css(\'display\', (this.checked ? \'block\' : \'none\') )',
-                  ),
-                 */
-                ),
-            );
-            
+                    ],
+                    /*
+                     * see deploy method to handle an action
+                      'sshproxy' => [
+                        'label' => t("deploymethod-sshproxy"),
+                        'checked' => $sDeploymethod==="sshproxy",
+                        'onclick' => '$(\'#'.$sDivId4TargetHosts.'\').css(\'display\', (this.checked ? \'block\' : \'none\') )',
+                        ],
+                     */
+                ],
+            ];
+
 
-            $aForms["setup"]["form"]['input' . $i++] = array(
+            $aForms["setup"]["form"]['input' . $i++] = [
                 'type' => 'markup',
                 'value' => ''
-                . '<div id="' . $sDivId4TargetHosts . '" ' . ($sDeploymethod !== "none" ? '' : ' style="display: none;"') . '>'
-            );
-            
+                    . '<div id="' . $sDivId4TargetHosts . '" ' . ($sDeploymethod !== "none" ? '' : ' style="display: none;"') . '>'
+            ];
+
             // rollout plugin: phase specific overrides
-            $aForms["setup"]["form"]['input' . $i++] = array(
+            $aForms["setup"]["form"]['input' . $i++] = [
                 'type' => 'markup',
                 'value' => ''
                     // . '<hr>'
-                    .'<label class="col-sm-2">'.t('deploy-rollout-plugin-config') .'</label>'
-                    .'<div class="col-sm-10">'.$aRollout[$sPhase].'</div>'
-            ); 
-            
-            $aForms["setup"]["form"]['input' . $i++] = array(
+                    . '<label class="col-sm-2">' . t('deploy-rollout-plugin-config') . '</label>'
+                    . '<div class="col-sm-10">' . $aRollout[$sPhase] . '</div>'
+            ];
+
+            $aForms["setup"]["form"]['input' . $i++] = [
                 'type' => 'text',
                 'name' => 'phases[' . $sPhase . '][hosts]',
                 'label' => $this->_oHtml->getIcon('host') . t("phase-targethosts"),
@@ -1359,7 +1396,7 @@ class projectgui extends project {
                 'validate' => 'isastring',
                 'size' => 100,
                 'placeholder' => 'FQDN1,FQDN2',
-            );
+            ];
 
             /*
               if ($sPuppethost) {
@@ -1379,18 +1416,18 @@ class projectgui extends project {
               } else {
               $sOut = '<span class="ok">' . sprintf(t("class-project-info-setup-ssh-and-puppet-ok"), $sPuppethost) . '</span>';
               }
-              $aForms["setup"]["form"]['input' . $i++] = array(
+              $aForms["setup"]["form"]['input' . $i++] = [
               'type' => 'markup',
               'value' => '<div class="form-group">'
               . '<label class="col-sm-2"> </label><div class="col-sm-10">'
               . $sOut
               . '</div></div>',
-              );
+            ];
               }
              */
 
             // when to deploy
-            $aForms["setup"]["form"]['input' . $i++] = array(
+            $aForms["setup"]["form"]['input' . $i++] = [
                 'type' => 'text',
                 'name' => 'phases[' . $sPhase . '][deploytimes]',
                 'label' => $this->_oHtml->getIcon('time') . t("deploytimes"),
@@ -1399,92 +1436,94 @@ class projectgui extends project {
                 'validate' => 'isastring',
                 'size' => 100,
                 'placeholder' => isset($this->_aConfig["phases"][$sPhase]["deploytimes"]) ? implode(", ", $this->_aConfig["phases"][$sPhase]["deploytimes"]) : '',
-            );
-            $aForms["setup"]["form"]['input' . $i++] = array(
+            ];
+            $aForms["setup"]["form"]['input' . $i++] = [
                 'type' => 'markup',
                 'value' => ''
-                . '</div>'
-            );
+                    . '</div>'
+            ];
 
             if ($aSelectForemanGroups) {
                 $aForms["setup"]["form"]['input' . $i++] = $aSelectForemanHostGroup;
             }
 
-            $aForms["setup"]["form"]['input' . $i++] = array(
+            $aForms["setup"]["form"]['input' . $i++] = [
                 'type' => 'markup',
                 'value' => ''
-                . '</div>'
-            ); // close div for active phase
+                    . '</div>'
+            ]; // close div for active phase
 
 
-            $aForms["setup"]["form"]['input' . $i++] = array(
+            $aForms["setup"]["form"]['input' . $i++] = [
                 'type' => 'markup',
                 'value' => '</td></tr></tbody></table>',
-            );
+            ];
         } // END: loop over phases
 
         // --------------------------------------------------
         // Tab for raw data
         // --------------------------------------------------
-        
-        $sRolloutDebug='<hr>DEBUG:<br>';
+
+        $sRolloutDebug = '<hr>DEBUG:<br>';
         foreach (array_keys($this->getPhases()) as $sPhase) {
-            if ($this->isActivePhase($sPhase)){
-                $sRolloutDebug.='<strong>'.$sPhase.'</strong>'
-                . '<pre>Config = '.print_r($this->oRolloutPlugin->getConfig($sPhase, 1), 1).'</pre>'
-                . '<pre>Commands = '.print_r($this->oRolloutPlugin->getDeployCommands($sPhase, 1), 1).'</pre>'
+            if ($this->isActivePhase($sPhase)) {
+                $sRolloutDebug .= '<strong>' . $sPhase . '</strong>'
+                    . '<pre>Config = ' . print_r($this->oRolloutPlugin->getConfig($sPhase, 1), 1) . '</pre>'
+                    . '<pre>Commands = ' . print_r($this->oRolloutPlugin->getDeployCommands($sPhase, 1), 1) . '</pre>'
                 ;
             }
         }
 
-        $aForms["setup"]["form"]['input' . $i++] = array(
+        $aForms["setup"]["form"]['input' . $i++] = [
             'type' => 'markup',
             'value' => '</div>'
-            
+
                 . '<div class="tab-pane fade" id="tab6">'
-                . '<br><pre>'.print_r($this->_aPrjConfig, 1).'</pre>'
+                . '<br><pre>' . print_r($this->_aPrjConfig, 1) . '</pre>'
                 . $sRolloutDebug
                 . '</div>'
-            
-            . '</div>'
-            . '</div>'
-            . '<div style="clear: both; margin-bottom: 1em;"></div>'
-            
-            
-            . '<hr>',
-        );
-        $aForms["setup"]["form"]['input' . $i++] = array(
+
+                . '</div>'
+                . '</div>'
+                . '<div style="clear: both; margin-bottom: 1em;"></div>'
+
+
+                . '<hr>',
+        ];
+        $aForms["setup"]["form"]['input' . $i++] = [
             'type' => 'submit',
             'name' => 'btnsave',
             'label' => t("save"),
-            'value' => $this->_oHtml->getIcon('sign-ok').t("save"),
-        );
+            'value' => $this->_oHtml->getIcon('sign-ok') . t("save"),
+        ];
 
         $oForm = new formgen($aForms);
         return $sMessages . $oForm->renderHtml("setup");
     }
 
     /**
-     * return html code for the installed version in the repository
+     * Get html code for the installed version in the repository
      * @param boolean  $bRefresh  optional: refresh flag; default: use cached information
      * @return string
      */
-    public function renderRepoInfo($bRefresh=false) {
+    public function renderRepoInfo(bool $bRefresh = false): string
+    {
         $sReturn = "";
         switch ($this->_aPrjConfig["build"]["type"]) {
             case "git":
 
                 $aRepodata = $this->getRepoRevision($bRefresh);
                 if (array_key_exists("revision", $aRepodata)) {
-                    $sReturn.=$this->_getChecksumDiv($aRepodata["revision"],
+                    $sReturn .= $this->_getChecksumDiv(
+                        $aRepodata["revision"],
                         $this->_oHtml->getIconByType('branch') . t('branch') . ': ' . (array_key_exists("branch", $aRepodata) ? $aRepodata["branch"] : '-') . '<br>'
                         . $this->_oHtml->getIconByType('revision') . t('revision') . ': ' . $this->_renderRevision($aRepodata["revision"]) . '<br>'
                         . $this->_oHtml->getIconByType('comment') . t('commitmessage') . ':<br>'
-                        )
-                        ."<pre>" . htmlentities($aRepodata["message"]). "</pre>";
+                    )
+                        . "<pre>" . htmlentities($aRepodata["message"]) . "</pre>";
                 } else {
                     $sReturn .= $this->_oHtml->getBox("error", sprintf(t('class-project-error-no-repoinfo'), $aRepodata["error"]))
-                            . $this->renderLink("setup") . '<br>';
+                        . $this->renderLink("setup") . '<br>';
                 }
 
                 break;
@@ -1493,36 +1532,40 @@ class projectgui extends project {
                 $sReturn .= $this->_oHtml->getBox("error", sprintf(t('class-project-error-wrong-buildtype'), $this->_aPrjConfig["build"]["type"]));
         }
         if (array_key_exists("url", $this->_aPrjConfig["build"])) {
-            $sReturn.=t('repository-url') . ': ' . $this->_aPrjConfig["build"]["url"] . '<br>';
+            $sReturn .= t('repository-url') . ': ' . $this->_aPrjConfig["build"]["url"] . '<br>';
         }
         if (array_key_exists("webaccess", $this->_aPrjConfig["build"])) {
-            $sReturn.=t('repository-access-browser') . ':<br><a href="' . $this->_aPrjConfig["build"]["webaccess"] . '">' . $this->_aPrjConfig["build"]["webaccess"] . '</a><br>';
+            $sReturn .= t('repository-access-browser') . ':<br><a href="' . $this->_aPrjConfig["build"]["webaccess"] . '">' . $this->_aPrjConfig["build"]["webaccess"] . '</a><br>';
         }
         return $sReturn;
     }
 
     /**
-     * get html code for a link to the commit
+     * Get html code for a link to the commit
      * (works for guithub and gitlab instances)
      * 
-     * @param string $sRevision
+     * @param null|string  $sRevision
      * @return string
      */
-    public function _renderRevision($sRevision) {
+    public function _renderRevision(null|string $sRevision): string
+    {
         $sUrl = str_replace('/tree/master', '', $this->_aPrjConfig["build"]["webaccess"]) . '/commit/' . $sRevision;
         return '<a href="' . $sUrl . '">' . $sRevision . '</a>';
-        return $sUrl;
+        // return $sUrl;
     }
+
     /**
-     * get html form with selectr for remote branches
+     * Get html form with selectr for remote branches
+     * 
      * @param string $sActiveBranchname  force active branch name
      * @param bool $bIgnoreCache  flag to ignore exiting cached data
      * @return string
      */
-    public function renderSelectRemoteBranches($sActiveBranchname = false, $bIgnoreCache=false) {
-        $this->log(__FUNCTION__."(sActiveBranchname = $sActiveBranchname, bIgnoreCache = ".($bIgnoreCache ? 'true' : 'false').") start");
-        $aReturn = array();
-        $aRadios = array();
+    public function renderSelectRemoteBranches(string $sActiveBranchname = '', bool $bIgnoreCache = false): string
+    {
+        $this->log(__FUNCTION__ . "(sActiveBranchname = $sActiveBranchname, bIgnoreCache = " . ($bIgnoreCache ? 'true' : 'false') . ") start");
+        $aReturn = [];
+        $aRadios = [];
         $bFoundActive = false;
         $i = 0;
         if (!$this->_oVcs) {
@@ -1539,10 +1582,10 @@ class projectgui extends project {
             }
             foreach ($this->_oVcs->getRemoteBranches($bIgnoreCache) as $aBranch) {
                 $sBranch = $aBranch['name'];
-                $aRadios[$sBranch] = array(
+                $aRadios[$sBranch] = [
                     'value' => $sBranch,
                     'label' => $aBranch['label'],
-                );
+                ];
                 // if no param was given the first branch will be marked
                 if (!$sActiveBranchname) {
                     $sActiveBranchname = $sBranch;
@@ -1556,23 +1599,24 @@ class projectgui extends project {
                     // not on the option (Chrome)
                     // $aRadios[$sBranch]['onclick'] = 'document.getElementById(\'submitBranch\').click()';
                 }
-            };
+            }
+            ;
         }
         // no branches were found
         if (count($aRadios) == 0) {
             return '';
         }
 
-        $aForms = array(
-            'frmSelectBranch' => array(
-                'meta' => array(
+        $aForms = [
+            'frmSelectBranch' => [
+                'meta' => [
                     'method' => 'POST',
                     'action' => '?',
                     'id' => 'frmSelectBranch',
-                ),
-                'validate' => array(),
-                'form' => array(
-                    'branchname' => array(
+                ],
+                'validate' => [],
+                'form' => [
+                    'branchname' => [
                         'inline' => true,
                         'type' => 'select',
                         'onchange' => 'document.getElementById(\'submitBranch\').click()',
@@ -1580,33 +1624,34 @@ class projectgui extends project {
                         'label' => '<strong>' . t('branch-select') . '</strong>',
                         'validate' => 'isastring',
                         'options' => $aRadios,
-                    ),
-                ),
-            ),
-        );
+                    ],
+                ],
+            ],
+        ];
 
         // submit to switch branches - only if a selection is available
         if (count($aRadios) > 1 || !$bFoundActive) {
-            $aForms['frmSelectBranch']['form']['submitBranch'] = array(
+            $aForms['frmSelectBranch']['form']['submitBranch'] = [
                 'type' => 'submit',
                 'name' => 'btnsave',
                 'onclick' => 'showModalMessage(\'' . t('branch-switch') . '\'); ',
                 'label' => t("change"),
-                'value' => $this->_oHtml->getIcon('sign-ok').t("change"),
-            );
+                'value' => $this->_oHtml->getIcon('sign-ok') . t("change"),
+            ];
         }
 
         $oFrm = new formgen($aForms);
         return $oFrm->renderHtml('frmSelectBranch')
-                . '<script>$("#submitBranch").hide();</script>';
+            . '<script>$("#submitBranch").hide();</script>';
         // return $oFrm->renderHtmlElement('dummy',$aFormData);
     }
 
     /**
-     * return html code for a list of all built packages and their usage
+     * Get html code for a list of all built packages and their usage
      * @return string
      */
-    public function renderVersionUsage() {
+    public function renderVersionUsage(): string
+    {
         $sReturn = false;
         $sRowHead1 = false;
         $sRowHead2 = '<td></td>';
@@ -1617,26 +1662,26 @@ class projectgui extends project {
         }
 
         foreach ($this->getActivePhases() as $sPhase) {
-            $sRowHead1.='<th class="' . $sPhase . ' tdphase" colspan="' . (count($this->_aPlaces) + 1) . '">' . $sPhase . '</th>';
-            $sRowHead2.='<td></td>' . $this->renderPlacesAsTd($sPhase);
+            $sRowHead1 .= '<th class="' . $sPhase . ' tdphase" colspan="' . (count($this->_aPlaces) + 1) . '">' . $sPhase . '</th>';
+            $sRowHead2 .= '<td></td>' . $this->renderPlacesAsTd($sPhase);
         }
-        
+
         krsort($aAllVersions);
         foreach ($aAllVersions as $sVersion => $aData) {
-            $sReturn.='<tr>';
-
-            $sInfos = $this->renderInfoLink($aData["info"], array('hpos' => 'left'));
-            $sReturn.='<td>'
-                        . $this->_getChecksumDiv(
-                            $aData['info']['revision'],
-                            $this->_oHtml->getIconByType('calendar') . t('build-from') . ': ' . $sVersion .'<br>'
-                                . $this->_oHtml->getIconByType('branch') . t('branch') . ': ' . $aData['info']["branch"] . '<br>'
-                                . $this->_oHtml->getIconByType('revision') . t('revision') . ': ' . $this->_renderRevision($aData['info']["revision"]) . '<br>'
-                          )
-                    . '</td><td>'
-                    . '&nbsp;&nbsp;' . $sInfos . '&nbsp;&nbsp;'
-                    . '</td>'
-                    ;
+            $sReturn .= '<tr>';
+
+            $sInfos = $this->renderInfoLink($aData["info"], ['hpos' => 'left']);
+            $sReturn .= '<td>'
+                . $this->_getChecksumDiv(
+                    $aData['info']['revision'],
+                    $this->_oHtml->getIconByType('calendar') . t('build-from') . ': ' . $sVersion . '<br>'
+                    . $this->_oHtml->getIconByType('branch') . t('branch') . ': ' . $aData['info']["branch"] . '<br>'
+                    . $this->_oHtml->getIconByType('revision') . t('revision') . ': ' . $this->_renderRevision($aData['info']["revision"]) . '<br>'
+                )
+                . '</td><td>'
+                . '&nbsp;&nbsp;' . $sInfos . '&nbsp;&nbsp;'
+                . '</td>'
+            ;
 
             foreach ($this->getActivePhases() as $sPhase) {
                 $sTLine = '';
@@ -1644,51 +1689,52 @@ class projectgui extends project {
 
                 // $sReturn.=$aData["rollback"][$sPhase] ? '<td>'.$this->renderLink("rollback", $sPhase, $sVersion).'</td>' : '<td>Rollback NOT possible</td>';
                 // $sReturn.=$aData["rollback"][$sPhase] ? '<td> Y </td>' : '<td> N </td>';
-                $sReturn.='<td>  </td>';
+                $sReturn .= '<td>  </td>';
 
                 foreach (array_keys($this->_aPlaces) as $sPlace) {
                     $bFound = false;
-                    $sReturn.=$aData["usage"][$sPhase][$sPlace] 
-                            ? '<td class="' . $sPhase . '" style="text-align: center;">'
-                            . $this->_getChecksumDiv($aData['info']['revision'], 'X')
-                            . '</td>' 
-                            : '<td> </td>'
-                        ;
+                    $sReturn .= $aData["usage"][$sPhase][$sPlace]
+                        ? '<td class="' . $sPhase . '" style="text-align: center;">'
+                        . $this->_getChecksumDiv($aData['info']['revision'], 'X')
+                        . '</td>'
+                        : '<td> </td>'
+                    ;
                 }
             }
-            $sReturn.='</tr>';
+            $sReturn .= '</tr>';
         }
 
         $sReturn = t('class-project-info-table-packages') . '<br><br>'
-                . '<table>'
-                . '<thead><tr><td>Version</td><td></td>'
-                . $sRowHead1
-                . '</tr><tr><td>'
-                . $sRowHead2
-                . '</tr></thead>'
-                . '<tbody>'
-                . $sReturn
-                . '</tbody>'
-                . '</table>';
+            . '<table>'
+            . '<thead><tr><td>Version</td><td></td>'
+            . $sRowHead1
+            . '</tr><tr><td>'
+            . $sRowHead2
+            . '</tr></thead>'
+            . '<tbody>'
+            . $sReturn
+            . '</tbody>'
+            . '</table>';
         return $sReturn;
     }
 
     /**
-     * render graphical overview of process (in project overview)
+     * Get html code for graphical overview of process (in project overview)
      * @return string
      */
-    public function renderVisual() {
+    public function renderVisual(): string
+    {
         $sReturn = '';
 
-        $renderAdminLTE=new renderadminlte();
+        $renderAdminLTE = new renderadminlte();
 
         $sBarHeightBg = '1.0em';
         $sBarHeight = '0.8em';
         $sContinue = '<span style="font-size: 300%; color:#ace;">&raquo;&raquo;</span><br>';
 
-        $aBranches=$this->getRemoteBranches();
-        if(!is_array($aBranches)){
-            return '<br>'.$this->_oHtml->getBox("error", t("project-setup-incomplete"));
+        $aBranches = $this->getRemoteBranches();
+        if (!is_array($aBranches)) {
+            return '<br>' . $this->_oHtml->getBox("error", t("project-setup-incomplete"));
         }
 
         $sRepoBar = '';
@@ -1706,8 +1752,8 @@ class projectgui extends project {
         $sPackagebar = '';
         $aVersions = $this->_getVersionUsage();
         foreach ($aVersions as $sVersion => $aData) {
-            $sBar = $aData["info"]["revision"] ? $this->_getChecksumDiv($aData["info"]["revision"], '' , $sBarHeight) : '';
-            $sPackagebar.='<span title="' . $sVersion . '" style="float: left; background:#eee; height: '.$sBarHeightBg.'; width:' . (100 / count($aVersions)) . '%">' . $sBar . '&nbsp;</span>';
+            $sBar = $aData["info"]["revision"] ? $this->_getChecksumDiv($aData["info"]["revision"], '', $sBarHeight) : '';
+            $sPackagebar .= '<span title="' . $sVersion . '" style="float: left; background:#eee; height: ' . $sBarHeightBg . '; width:' . (100 / count($aVersions)) . '%">' . $sBar . '&nbsp;</span>';
         }
 
         $sPhaseImg = '';
@@ -1718,18 +1764,18 @@ class projectgui extends project {
                 if ($this->canAcceptPhase($sLastPhase)) {
                     $sAction .= $this->renderLink("accept", $sLastPhase);
                 }
-                $sPhaseImg.='<div class="action">' . $sAction . '</div>';
+                $sPhaseImg .= '<div class="action">' . $sAction . '</div>';
             }
             $sLastPhase = $sPhase;
 
             $sFullbar = '';
             foreach (array_keys($this->_aPlaces) as $sPlace) {
-                $sFullbar.='<span title="' . $this->_aPlaces[$sPlace] . '" style="float: left; background:#eee; height: '.$sBarHeightBg.'; width:' . (100 / count($this->_aPlaces)) . '%">' . $this->_renderBar($sPhase, $sPlace, $sBarHeight) . '&nbsp;</span>';
+                $sFullbar .= '<span title="' . $this->_aPlaces[$sPlace] . '" style="float: left; background:#eee; height: ' . $sBarHeightBg . '; width:' . (100 / count($this->_aPlaces)) . '%">' . $this->_renderBar($sPhase, $sPlace, $sBarHeight) . '&nbsp;</span>';
             }
             // $sDetail = $sFullbar . '<br><a href="#h3phases" class="scroll-link">' . $sPhase . '</a>';
             $sDetail = $sFullbar . '<br>' . $sPhase;
 
-            $sPhaseImg.='
+            $sPhaseImg .= '
             <div class="process">
                 <div class="details">' . $sDetail . ' </div>
             </div>';
@@ -1740,55 +1786,61 @@ class projectgui extends project {
             . $renderAdminLTE->addRow(
                 ''
                 . $renderAdminLTE->addCol(
-                    $renderAdminLTE->getCard([
-                        'type'=>'',
-                        'variant'=>'outline',
-                        'title'=>'',
-                        'text'=> '<h3>'.t("versioncontrol"). '</h3>' . $sRepoBar . '<br>
+                    $renderAdminLTE->getCard(
+                        [
+                            'type' => '',
+                            'variant' => 'outline',
+                            'title' => '',
+                            'text' => '<h3>' . t("versioncontrol") . '</h3>' . $sRepoBar . '<br>
                             ' . t("repositoryinfos") . '<br>
                             ' // . $this->_aPrjConfig["build"]["type"] 
-                            . preg_replace('/.*\@(.*):.*/', '$1', $this->_aPrjConfig["build"]["url"])
-                            . '<br>(<strong title="' . t('branch-select') . '">' . count($aBranches) . '</strong>)',
+                                . preg_replace('/.*\@(.*):.*/', '$1', $this->_aPrjConfig["build"]["url"])
+                                . '<br>(<strong title="' . t('branch-select') . '">' . count($aBranches) . '</strong>)',
                         ]
-                    ), 2
-                    )
+                    ),
+                    2
+                )
                 . $renderAdminLTE->addCol(
                     '<div class="action">' . $sContinue . t("build-hint-overview") . '<br><br>' . ($this->canAcceptPhase() ? $this->renderLink("build") : '') . '</div>',
                     1
                 )
                 . $renderAdminLTE->addCol(
-                    $renderAdminLTE->getCard([
-                        'type'=>'',
-                        'variant'=>'outline',
-                        'title'=>'',
-                        'text'=> '<h3>'.$this->_oHtml->getIcon('package') . t("archive"). '</h3>' 
-                            . '<div class="archive">'
+                    $renderAdminLTE->getCard(
+                        [
+                            'type' => '',
+                            'variant' => 'outline',
+                            'title' => '',
+                            'text' => '<h3>' . $this->_oHtml->getIcon('package') . t("archive") . '</h3>'
+                                . '<div class="archive">'
                                 . '<div class="details">'
-                                . $sPackagebar . '<br>' 
+                                . $sPackagebar . '<br>'
                                 . '</div>'
                                 . t("packages") . '<br>'
-                                .'(<strong>' . count($this->_getVersionUsage()) . '</strong>)'
-                            . '</div>'
+                                . '(<strong>' . count($this->_getVersionUsage()) . '</strong>)'
+                                . '</div>'
                         ]
-                    ), 2
-                    )
+                    ),
+                    2
+                )
                 . $renderAdminLTE->addCol(
-                    '<div class="action">'.$sContinue . sprintf(t("queue-hint-overview"), $this->getNextPhase()).'</div>',
+                    '<div class="action">' . $sContinue . sprintf(t("queue-hint-overview"), $this->getNextPhase()) . '</div>',
                     1
                 )
                 . $renderAdminLTE->addCol(
-                    $renderAdminLTE->getCard([
-                        'type'=>'',
-                        'variant'=>'outline',
-                        'title'=>'',
-                        'text'=> '<h3>'.$this->_oHtml->getIcon('phase') . t("phases"). '</h3>' 
-                            . ($sPhaseImg ? $sPhaseImg : '<div class="process">' . t("none") . '</div>')
+                    $renderAdminLTE->getCard(
+                        [
+                            'type' => '',
+                            'variant' => 'outline',
+                            'title' => '',
+                            'text' => '<h3>' . $this->_oHtml->getIcon('phase') . t("phases") . '</h3>'
+                                . ($sPhaseImg ? $sPhaseImg : '<div class="process">' . t("none") . '</div>')
                         ]
-                    ), 6
-                    )
+                    ),
+                    6
                 )
-                .'</div>'
-            ;
+            )
+            . '</div>'
+        ;
 
         return $sReturn;
     }
diff --git a/public_html/deployment/classes/projectlist.class.php b/public_html/deployment/classes/projectlist.class.php
index 155575b017c0453a8398761707072ed9ee3bca29..577c98ffc9d6fd2d280887bc1c576f1483e9652a 100644
--- a/public_html/deployment/classes/projectlist.class.php
+++ b/public_html/deployment/classes/projectlist.class.php
@@ -7,7 +7,9 @@
   class projectlist to render overview page
 
   ---------------------------------------------------------------------
-  2013-11-08  Axel <axel.hahn@iml.unibe.ch>
+  Axel <axel.hahn@unibe.ch>
+  2013-11-08  Axel
+  2024-08-29  Axel php8 only; added variable types; short array syntax
   ###################################################################### */
 
 require_once 'base.class.php';
@@ -50,7 +52,7 @@ class projectlist extends base
      * render html for overview table
      * @return string
      */
-    public function renderOverview()
+    public function renderOverview(): string
     {
         global $renderAdminLTE;
 
@@ -91,7 +93,7 @@ class projectlist extends base
         $oPrj = new projectgui();
         $aProjectByLabel = $oPrj->getProjects("label");
         foreach ($aProjectByLabel as $aProject) {
-            $sPrj=$aProject['id'];
+            $sPrj = $aProject['id'];
             $oPrj = new projectgui($sPrj);
             $sPrjFilter .= '<option value="' . $sPrj . '">' . $aProject['label'] . '</option>';
 
@@ -147,32 +149,32 @@ class projectlist extends base
             // render output
             $sOut .= '
                 <tr class="' . $sClasses . '" '
-                    . 'ondblclick="location.href=\'/deployment/' . $sPrj . '/\'" '
-                    . 'title="' . sprintf(t("overview-hint-dblclick"), $sPrj) 
+                . 'ondblclick="location.href=\'/deployment/' . $sPrj . '/\'" '
+                . 'title="' . sprintf(t("overview-hint-dblclick"), $sPrj)
                 . '">
                     <td class="prj">
                         <span class="float-right"><i class="fa-solid fa-tag"></i> ' . $sPrjGroup . '</span>
                         <strong>'
-                . $oHtml->getLink(array(
+                . $oHtml->getLink([
                     'href' => '/deployment/' . $sPrj . '/',
                     // 'title' => $oPrj->getDescription(),
                     'icon' => 'project',
                     'label' => $oPrj->getLabel()
-                ))
+                ])
                 . '</strong>'
                 . '<div class="descr">' . $oPrj->getDescription() . '</div>'
                 . ($sProgress ? '<div class="deployprogress">' . $sProgress . '</div>' : '')
                 // . '    <br>'
                 . '</td>'
                 . '<td class="prj">'
-                . $oHtml->getLinkButton(array(
+                . $oHtml->getLinkButton([
                     'href' => '#',
                     'onclick' => 'setProjectFilter(\'' . $sPrj . '\'); return false;',
                     'style' => 'float: right',
                     'title' => t("overview-filter-hint"),
                     'icon' => 'filter',
                     'label' => t("overview-filter"),
-                ))
+                ])
                 . '</td>'
                 . '<td class="prj">';
             if ($oPrj->canAcceptPhase()) {
@@ -208,14 +210,7 @@ class projectlist extends base
                 <div id="counter">...</div><br>
 
                 <div class="filterbar">        
-                    <form class="form-inline">
-
-                        <!--
-                        <a href="#" class="view viewextended" onclick="setview(\'simple\');"><i class="glyphicon glyphicon-th-large"></i> ' . t("overview-simpleview") . '</a>
-                        <a href="#" class="view viewsimple" onclick="setview(\'extended\');" ><i class="glyphicon glyphicon-th-list"></i> ' . t("overview-extview") . '</a>
-                        |
-                        -->
-                        
+                    <form class="form-inline">                        
 
                         <label for="efilter">
                             ' . $oHtml->getIcon('filter') . '
@@ -321,7 +316,7 @@ class projectlist extends base
 			</thead>
 			<tbody>
 		' . $sOut . '</tbody></table>'
-        ;
+            ;
         } else {
             $sOut = t("class-pl-error-no-project") . '<br><br>'
                 . $oPrj1->renderLink("new");
diff --git a/public_html/deployment/classes/queryparam.class.php b/public_html/deployment/classes/queryparam.class.php
index f4dd62ceabc92f29da08ebdb1a16759fc6d9bcbf..fd6b4448e9da5a6bd8da259672c8c732c154605e 100644
--- a/public_html/deployment/classes/queryparam.class.php
+++ b/public_html/deployment/classes/queryparam.class.php
@@ -1,35 +1,53 @@
 <?php
 
+/**
+ * Wrapping class to access $_GET|$_POST|$_SESSION variables with validation 
+ * of a value
+ * 
+ * @example
+ * Ensure that https://example.com/?page=<VALUE> will be accepted only if 
+ * <VALUE> matches /^[a-z]*$/
+ * <code>queryparam::get('page', '/^[a-z]*$/');</code>
+ * 
+ * Ideas:
+ * - add more type validation
+ * - add regex param with chars to remove eg /[^a-z]/ to keep only lowercase letters
+ * 
+ * Axel <axel.hahn@unibe.ch>
+ * 2024-08-29  Axel php8 only; added variable types; short array syntax
+ */
 
-class queryparam{
-
+class queryparam
+{
 
     /**
-     * return value of a a given scope variable (e.g. $_GET|$_POST|$_SESSION) if it exists.
+     * Get value of a a given scope variable (e.g. $_GET|$_POST|$_SESSION) if it exists.
      * It will return NULLL if the value does not match an optional regex or type.
      * 
+     * @param array   $aScope        array of scope variables
      * @param string  $sVarname      name of post or get variable (POST has priority)
      * @param string  $sRegexMatch   set a regex that must match
      * @param string  $sType         force type: false|int
      * @return mixed NULL|value
      */
-    static public function getvar($aScope, $sVarname, $sRegexMatch=false, $sType=false){
+    static public function getvar(array $aScope, string $sVarname, string $sRegexMatch = '', string $sType = ''): mixed
+    {
         // check if it exist
-        if(!isset($aScope[$sVarname])){
+        if (!isset($aScope[$sVarname])) {
             return NULL;
         }
-        
+
         $return = $aScope[$sVarname];
-        
+
         // verify regex
-        if ($sRegexMatch && !preg_match($sRegexMatch,$return)){
+        if ($sRegexMatch && !preg_match($sRegexMatch, $return)) {
             return NULL;
         }
-        
+
         // force given type
-        switch ($sType){
-            case 'int': 
-                $return=(int)$return;
+        switch ($sType) {
+            case 'int':
+                $return = (int) $return;
                 break;
         }
         return $return;
@@ -44,7 +62,8 @@ class queryparam{
      * @param string  $sType         force type: false|int
      * @return mixed NULL|value
      */
-    static function get($sVarname, $sRegexMatch=false, $sType=false) {
+    static function get(string $sVarname, string $sRegexMatch = '', string $sType = ''): mixed
+    {
         return self::getvar($_GET, $sVarname, $sRegexMatch, $sType);
     }
 
@@ -56,23 +75,21 @@ class queryparam{
      * @param string  $sType         force type: false|int
      * @return mixed NULL|value
      */
-    static function getorpost($sVarname, $sRegexMatch=false, $sType=false) {
+    static function getorpost(string $sVarname, string $sRegexMatch = '', string $sType = ''): mixed
+    {
         // $this->logAdd(__METHOD__."($sVarname, $sRegexMatch, $sType) start");
-        
+
         // check if it exist
-        if(!isset($_POST[$sVarname]) && !isset($_GET[$sVarname])){
+        if (!isset($_POST[$sVarname]) && !isset($_GET[$sVarname])) {
             // $this->logAdd(__METHOD__."($sVarname) $sVarname does not exist");
             return false;
         }
-        
+
         // set it to POST or GET variable
-        $aScope = isset($_POST[$sVarname]) && $_POST[$sVarname]
-                ? $_POST[$sVarname] 
-                : ((isset($_GET[$sVarname]) && $_GET[$sVarname])
-                    ? $_GET[$sVarname] 
-                    : false
-                  )
-            ;
+        $aScope = isset($_POST[$sVarname])
+            ? $_POST
+            : $_GET
+        ;
         return self::getvar($aScope, $sVarname, $sRegexMatch, $sType);
 
     }
@@ -84,7 +101,8 @@ class queryparam{
      * @param string  $sType         force type: false|int
      * @return mixed NULL|value
      */
-    static function post($sVarname, $sRegexMatch=false, $sType=false) {
+    static function post(string $sVarname, string $sRegexMatch = '', string $sType = ''): mixed
+    {
         return self::getvar($_POST, $sVarname, $sRegexMatch, $sType);
     }
 
diff --git a/public_html/deployment/classes/render-adminlte.class.php b/public_html/deployment/classes/render-adminlte.class.php
index 5ce190406a400514ec4e34ae8ca3f40233346ce5..83cf626c624afc23bc6237422c52be5c2b01d7b4 100755
--- a/public_html/deployment/classes/render-adminlte.class.php
+++ b/public_html/deployment/classes/render-adminlte.class.php
@@ -1,155 +1,171 @@
 <?php
 require_once 'htmlelements.class.php';
 /**
- * ======================================================================
+ * ______________________________________________________________________
+ * 
+ *     _  __  __  _    
+ *    | ||  \/  || |__     Institute for Medical Education
+ *    |_||_|\/|_||____|    University of Bern
+ * 
+ * ______________________________________________________________________
  * 
  * RENDERER FOR ADNINLTE template https://adminlte.io
- * DOCS: https://adminlte.io/docs/3.2/
- *       https://adminlte.io/themes/v3/index3.html
+ * its docs: https://adminlte.io/docs/3.2/
+ *           https://adminlte.io/themes/v3/index3.html
  * 
+ * This is a php class to render
+ * - grid layout
+ * - navigation
+ * - widgets, components and forms
+ * 
+ * DOCS: https://os-docs.iml.unibe.ch/adminlte-renderer/
  * ----------------------------------------------------------------------
  * 2023-09-11  <axel.hahn@unibe.ch>  add shadows on card + callout
  * 2023-09-27  <axel.hahn@unibe.ch>  add form input fields
  * 2023-11-17  <axel.hahn@unibe.ch>  add tabbed content; "=" renders hamburger item
+ * 2024-05-03  <axel.hahn@unibe.ch>  add line in sidebar menu; add getFormSelect
+ * 2024-05-10  <axel.hahn@unibe.ch>  add support for bootstrap-select in getFormSelect
+ * 2024-05-18  <axel.hahn@unibe.ch>  add variable types
+ * 2024-07-04  <axel.hahn@unibe.ch>  added type declarations
  * ======================================================================
- *
- * @author Axel
  */
-class renderadminlte {
+class renderadminlte
+{
 
-    var $aPresets=[
+    protected array $aPresets = [
 
-        'bgcolor'=>[
-            'description'=>'background colors',
-            'group'=>'styling',
-            'values'=>[
+        'bgcolor' => [
+            'description' => 'background colors',
+            'group' => 'styling',
+            'values' => [
                 // https://adminlte.io/themes/v3/pages/UI/general.html
-                ''=>'no value',
-                'indigo'=>'indigo',
-                'lightblue'=>'',
-                'navy'=>'',
-                'purple'=>'',
-                'fuchsia'=>'',
-                'pink'=>'',
-                'maroon'=>'',
-                'orange'=>'',
-                'lime'=>'',
-                'teal'=>'',
-                'olive'=>'',
-        
-                'black'=>'black',
-                'dark'=>'dark gray',
-                'gray'=>'gray', 
-                'light'=>'light gray', 
+                '' => 'no value',
+                'indigo' => 'indigo',
+                'lightblue' => '',
+                'navy' => '',
+                'purple' => '',
+                'fuchsia' => '',
+                'pink' => '',
+                'maroon' => '',
+                'orange' => '',
+                'lime' => '',
+                'teal' => '',
+                'olive' => '',
+
+                'black' => 'black',
+                'dark' => 'dark gray',
+                'gray' => 'gray',
+                'light' => 'light gray',
             ]
         ],
-    
-        'type'=>[
-            'description'=>'type or status like info/ warning/ danger to define a color',
-            'group'=>'styling',
-            'values'=>[
-                ''=>'no value',
-                'danger'=>'red',
-                'info'=>'aqua',
-                'primary'=>'blue',
-                'secondary'=>'gray',
-                'success'=>'green',
-                'warning'=>'yellow',
-                'dark'=>'dark gray',
-                'gray'=>'gray', 
+
+        'type' => [
+            'description' => 'type or status like info/ warning/ danger to define a color',
+            'group' => 'styling',
+            'values' => [
+                '' => 'no value',
+                'danger' => 'red',
+                'info' => 'aqua',
+                'primary' => 'blue',
+                'secondary' => 'gray',
+                'success' => 'green',
+                'warning' => 'yellow',
+                'dark' => 'dark gray',
+                'gray' => 'gray',
             ]
         ],
-        'shadow'=>[
-            'description'=>'use a shadow',
-            'group'=>'styling',
-            'values'=>[
-                ''=>'no value', 
-                'none'=>'none',
-                'small'=>'small', 
-                'regular'=>'regular',
-                'large'=>'large'
+        'shadow' => [
+            'description' => 'use a shadow',
+            'group' => 'styling',
+            'values' => [
+                '' => 'no value',
+                'none' => 'none',
+                'small' => 'small',
+                'regular' => 'regular',
+                'large' => 'large'
             ]
         ],
-        'size'=>[
-            'description'=>'set a size',
-            'group'=>'styling',
-            'values'=>[
-                ''=>'no value',
-                'lg'=>'',
-                'sm'=>'',
-                'xs'=>'',
-                'flat'=>'',
+        'size' => [
+            'description' => 'set a size',
+            'group' => 'styling',
+            'values' => [
+                '' => 'no value',
+                'lg' => '',
+                'sm' => '',
+                'xs' => '',
+                'flat' => '',
             ]
         ],
-        'variant'=>[
-            'description'=>'coloring style',
-            'group'=>'styling',
-            'values'=>[
-                ''=>'no value',
-                'outline'=>'small stripe on top',
-                'solid'=>'full filled widget',
-                'gradient'=>'full filled with gradient',
+        'variant' => [
+            'description' => 'coloring style',
+            'group' => 'styling',
+            'values' => [
+                '' => 'no value',
+                'outline' => 'small stripe on top',
+                'solid' => 'full filled widget',
+                'gradient' => 'full filled with gradient',
             ]
         ],
-        'visibility'=>[
-            'description'=>'',
-            'group'=>'customizing',
-            'values'=>[
-                ''=>'no value', 
-                '0'=>'hide', 
-                '1'=>'show',
+        'visibility' => [
+            'description' => '',
+            'group' => 'customizing',
+            'values' => [
+                '' => 'no value',
+                '0' => 'hide',
+                '1' => 'show',
             ]
         ],
         // for keys: state
-        'windowstate'=>[
-            'description'=>'state of a resizable widget',
-            'group'=>'customizing',
-            'values'=>[
-                ''=>'no value', 
-                'collapsed'=>'header only', 
-                'maximized'=>'full window',
+        'windowstate' => [
+            'description' => 'state of a resizable widget',
+            'group' => 'customizing',
+            'values' => [
+                '' => 'no value',
+                'collapsed' => 'header only',
+                'maximized' => 'full window',
             ]
         ],
         // for keys: dismissable
-        'yesno'=>[
-            'description'=>'',
-            'group'=>'customizing',
-            'values'=>[
-                ''=>'no value', 
-                '0'=>'no', 
-                '1'=>'yes',
+        'yesno' => [
+            'description' => '',
+            'group' => 'customizing',
+            'values' => [
+                '' => 'no value',
+                '0' => 'no',
+                '1' => 'yes',
             ]
         ],
     ];
 
-    var $_aValueMappings=[
-        'shadow'=>[
-            'default'  => '',
-            'none'     => 'shadow-none',
-            'small'    => 'shadow-small',
-            'regular'  => 'shadow',
-            'large'    => 'shadow-lg',
+    protected array $_aValueMappings = [
+        'shadow' => [
+            'default' => '',
+            'none' => 'shadow-none',
+            'small' => 'shadow-small',
+            'regular' => 'shadow',
+            'large' => 'shadow-lg',
         ]
     ];
 
-    var $_aElements=[];
-    
+    protected array $_aElements = [];
+
     /**
-     * instance of htmlelements
+     * instance of htmlelements object
      * @var object
      */
-    var $_oHtml=false;
-    
-    
+    protected object $_oHtml;
+
+
     // ----------------------------------------------------------------------
     // 
     // CONSTRUCTOR
     // 
     // ----------------------------------------------------------------------
-    public function __construct() {
-        $this->_oHtml=new htmlelements();
+    public function __construct()
+    {
+        $this->_oHtml = new htmlelements();
         $this->_initElements();
-        return true;
+        // return true;
     }
 
     // ----------------------------------------------------------------------
@@ -157,343 +173,380 @@ class renderadminlte {
     // PRIVATE FUNCTIONS 
     // 
     // ----------------------------------------------------------------------
-    
+
     /**
      * used in cosntructor
      * initialize all element definitions
+     * @return void
      */
-    protected function _initElements(){
-        $this->_aElements=[
+    protected function _initElements(): void
+    {
+        $this->_aElements = [
 
             // ------------------------------------------------------------
-            'alert'=>[
-                'label'=>'Alert',
-                'description'=>'Colored box with title and a text',
-                'method'=>'getAlert',
-        
-                'params'=>[
-                    'type'        => ['select'=>$this->aPresets['type'],     'example_value'=>'warning'],
-                    'dismissible' => ['select'=>$this->aPresets['yesno'],    'example_value'=>''],
-                    'class'       => [
-                        'group'=>'styling', 
-                        'description'=>'optional: css classes', 
-                        'example_value'=>''
+            'alert' => [
+                'label' => 'Alert',
+                'description' => 'Colored box with title and a text',
+                'method' => 'getAlert',
+
+                'params' => [
+                    'type' => ['select' => $this->aPresets['type'], 'example_value' => 'warning'],
+                    'dismissible' => ['select' => $this->aPresets['yesno'], 'example_value' => ''],
+                    'class' => [
+                        'group' => 'styling',
+                        'description' => 'optional: css classes',
+                        'example_value' => ''
                     ],
-                    'title'       => [
-                        'description'=>'Title in a bit bigger font', 
-                        'group'=>'content',
-                        'example_value'=>'Alert title'
+                    'title' => [
+                        'description' => 'Title in a bit bigger font',
+                        'group' => 'content',
+                        'example_value' => 'Alert title'
                     ],
-                    'text'        => [
-                        'description'=>'Message text', 
-                        'group'=>'content',
-                        'example_value'=>'I am a message. Read me, please.'
+                    'text' => [
+                        'description' => 'Message text',
+                        'group' => 'content',
+                        'example_value' => 'I am a message. Read me, please.'
                     ],
                 ]
             ],
             // ------------------------------------------------------------
-            'badge'=>[
-                'label'=>'Badge',
-                'description'=>'Tiny additional info; mostly as counter',
-                'method'=>'getBadge',
-        
-                'params'=>[
-                    'type'        => ['select'=>$this->aPresets['type'],     'example_value'=>'danger'],
-                    'bgcolor'     => ['select'=>$this->aPresets['bgcolor'],  'example_value'=>''],
-                    'class'       => [
-                        'group'=>'styling', 
-                        'description'=>'optional: css classes', 
-                        'example_value'=>''
+            'badge' => [
+                'label' => 'Badge',
+                'description' => 'Tiny additional info; mostly as counter',
+                'method' => 'getBadge',
+
+                'params' => [
+                    'type' => ['select' => $this->aPresets['type'], 'example_value' => 'danger'],
+                    'bgcolor' => ['select' => $this->aPresets['bgcolor'], 'example_value' => ''],
+                    'class' => [
+                        'group' => 'styling',
+                        'description' => 'optional: css classes',
+                        'example_value' => ''
                     ],
-                    'id'          => [
-                        'group'=>'customizing', 
-                        'description'=>'optional: id attribute', 
-                        'example_value'=>''
+                    'id' => [
+                        'group' => 'customizing',
+                        'description' => 'optional: id attribute',
+                        'example_value' => ''
                     ],
-                    'title'       => [
-                        'group'=>'content', 
-                        'description'=>'optional: title attribute for mouseover', 
-                        'example_value'=>'Errors: 5'
+                    'title' => [
+                        'group' => 'content',
+                        'description' => 'optional: title attribute for mouseover',
+                        'example_value' => 'Errors: 5'
                     ],
-                    'text'        => [
-                        'group'=>'content', 
-                        'description'=>'Text or value in the badge', 
-                        'example_value'=>'5'
+                    'text' => [
+                        'group' => 'content',
+                        'description' => 'Text or value in the badge',
+                        'example_value' => '5'
                     ],
                 ]
             ],
             // ------------------------------------------------------------
-            'button'=>[
-                'label'=>'Button',
-                'description'=>'Buttons<br>In this component you can add other parmeter keys too - these will be added as attributes in the button tag.',
-                'method'=>'getButton',
-        
-                'params'=>[
-                    'type'        => ['select'=>$this->aPresets['type'],     'example_value'=>'primary'],
-                    'size'        => ['select'=>$this->aPresets['size'],     'example_value'=>''],
-                    'class'       => [
-                        'group'=>'styling', 
-                        'description'=>'optional: css classes', 
-                        'example_value'=>''
+            'button' => [
+                'label' => 'Button',
+                'description' => 'Buttons<br>In this component you can add other parmeter keys too - these will be added as attributes in the button tag.',
+                'method' => 'getButton',
+
+                'params' => [
+                    'type' => ['select' => $this->aPresets['type'], 'example_value' => 'primary'],
+                    'size' => ['select' => $this->aPresets['size'], 'example_value' => ''],
+                    'class' => [
+                        'group' => 'styling',
+                        'description' => 'optional: css classes',
+                        'example_value' => ''
                     ],
-                    'text'        => [
-                        'group'=>'content', 
-                        'description'=>'Text/ html code on the button', 
-                        'example_value'=>'Click me'
+                    'text' => [
+                        'group' => 'content',
+                        'description' => 'Text/ html code on the button',
+                        'example_value' => 'Click me'
                     ],
                 ]
             ],
             // ------------------------------------------------------------
-            'callout'=>[
-                'label'=>'Callout',
-                'description'=>'Kind of infobox',
-                'method'=>'getCallout',
-        
-                'params'=>[
-                    'type'        => ['select'=>$this->aPresets['type'],     'example_value'=>'danger'],
-                    'shadow'      => ['select'=>$this->aPresets['shadow'],   'example_value'=>''],
-                    'class'       => [
-                        'group'=>'styling', 
-                        'description'=>'optional: css classes', 
-                        'example_value'=>''
+            'callout' => [
+                'label' => 'Callout',
+                'description' => 'Kind of infobox',
+                'method' => 'getCallout',
+
+                'params' => [
+                    'type' => ['select' => $this->aPresets['type'], 'example_value' => 'danger'],
+                    'shadow' => ['select' => $this->aPresets['shadow'], 'example_value' => ''],
+                    'class' => [
+                        'group' => 'styling',
+                        'description' => 'optional: css classes',
+                        'example_value' => ''
                     ],
-                    'title'       => [
-                        'group'=>'content', 
-                        'description'=>'Title in a bit bigger font', 
-                        'example_value'=>'I am a callout'
+                    'title' => [
+                        'group' => 'content',
+                        'description' => 'Title in a bit bigger font',
+                        'example_value' => 'I am a callout'
                     ],
-                    'text'        => [
-                        'group'=>'content',
-                        'description'=>'Message text', 
-                        'example_value'=>'Here is some description to whatever.'
+                    'text' => [
+                        'group' => 'content',
+                        'description' => 'Message text',
+                        'example_value' => 'Here is some description to whatever.'
                     ],
                 ]
             ],
             // ------------------------------------------------------------
-            'card'=>[
-                'label'=>'Card',
-                'description'=>'Content box with header, text, footer',
-                'method'=>'getCard',
-        
-                'params'=>[
-                    'type'        => ['select'=>$this->aPresets['type'],     'example_value'=>'primary'],
-                    'variant'     => ['select'=>$this->aPresets['variant'],  'example_value'=>'outline'],
-                    'shadow'      => ['select'=>$this->aPresets['shadow'],   'example_value'=>''],
-                    'class'       => [
-                        'group'=>'styling', 
-                        'description'=>'optional: css classes', 
-                        'example_value'=>''
+            'card' => [
+                'label' => 'Card',
+                'description' => 'Content box with header, text, footer',
+                'method' => 'getCard',
+
+                'params' => [
+                    'type' => ['select' => $this->aPresets['type'], 'example_value' => 'primary'],
+                    'variant' => ['select' => $this->aPresets['variant'], 'example_value' => 'outline'],
+                    'shadow' => ['select' => $this->aPresets['shadow'], 'example_value' => ''],
+                    'class' => [
+                        'group' => 'styling',
+                        'description' => 'optional: css classes',
+                        'example_value' => ''
                     ],
-                    'state'       => [
-                        'group'=>'customizing', 
-                        'select'=>$this->aPresets['windowstate'], 
-                        'example_value'=>''
+                    'state' => [
+                        'group' => 'customizing',
+                        'select' => $this->aPresets['windowstate'],
+                        'example_value' => ''
                     ],
-        
-                    'tb-collapse' => ['description'=>'show minus symbol as collapse button', 'select'=>$this->aPresets['visibility'], 'example_value'=>''],
-                    'tb-expand'   => ['description'=>'show plus symbol to expand card', 'select'=>$this->aPresets['visibility'], 'example_value'=>''],
-                    'tb-maximize' => ['description'=>'show maximize button for fullscreen', 'select'=>$this->aPresets['visibility'], 'example_value'=>''],
-                    'tb-minimize' => ['description'=>'show minimize button to minimize', 'select'=>$this->aPresets['visibility'], 'example_value'=>''],
-                    'tb-remove'   => ['description'=>'show cross symbol to remove card', 'select'=>$this->aPresets['visibility'], 'example_value'=>''],
-        
-                    'title'       => [
-                        'group'=>'content', 
-                        'description'=>'Title in the top row', 
-                        'example_value'=>'I am a card'
+
+                    'tb-collapse' => ['description' => 'show minus symbol as collapse button', 'select' => $this->aPresets['visibility'], 'example_value' => ''],
+                    'tb-expand' => ['description' => 'show plus symbol to expand card', 'select' => $this->aPresets['visibility'], 'example_value' => ''],
+                    'tb-maximize' => ['description' => 'show maximize button for fullscreen', 'select' => $this->aPresets['visibility'], 'example_value' => ''],
+                    'tb-minimize' => ['description' => 'show minimize button to minimize', 'select' => $this->aPresets['visibility'], 'example_value' => ''],
+                    'tb-remove' => ['description' => 'show cross symbol to remove card', 'select' => $this->aPresets['visibility'], 'example_value' => ''],
+
+                    'title' => [
+                        'group' => 'content',
+                        'description' => 'Title in the top row',
+                        'example_value' => 'I am a card'
                     ],
-                    'tools'       => [
-                        'group'=>'content', 
-                        'description'=>'Html code for the top right', 
-                        'example_value'=>''
+                    'tools' => [
+                        'group' => 'content',
+                        'description' => 'Html code for the top right',
+                        'example_value' => ''
                     ],
-                    'text'        => [
-                        'group'=>'content', 
-                        'description'=>'Main content', 
-                        'example_value'=>'Here is some beautiful content.'
+                    'text' => [
+                        'group' => 'content',
+                        'description' => 'Main content',
+                        'example_value' => 'Here is some beautiful content.'
                     ],
-                    'footer'      => [
-                        'group'=>'content', 
-                        'description'=>'optional: footer content', 
-                        'example_value'=>'Footer'
+                    'footer' => [
+                        'group' => 'content',
+                        'description' => 'optional: footer content',
+                        'example_value' => 'Footer'
                     ],
                 ]
             ],
             // ------------------------------------------------------------
-            'infobox'=>[
-                'label'=>'Info box',
-                'description'=>'Box with icon to highlight a single value; optional with a progress bar',
-                'method'=>'getInfobox',
-        
-                'params'=>[
-                    'type'=>['select'=>$this->aPresets['type'],     'example_value'=>''],
-                    'iconbg'=>['select'=>$this->aPresets['type'],   'example_value'=>'info'],
-                    'shadow'=>['select'=>$this->aPresets['shadow'], 'example_value'=>''],
-                    'class'       => [
-                        'group'=>'styling', 
-                        'description'=>'optional: css classes', 
-                        'example_value'=>''
+            'infobox' => [
+                'label' => 'Info box',
+                'description' => 'Box with icon to highlight a single value; optional with a progress bar',
+                'method' => 'getInfobox',
+
+                'params' => [
+                    'type' => ['select' => $this->aPresets['type'], 'example_value' => ''],
+                    'iconbg' => ['select' => $this->aPresets['type'], 'example_value' => 'info'],
+                    'shadow' => ['select' => $this->aPresets['shadow'], 'example_value' => ''],
+                    'class' => [
+                        'group' => 'styling',
+                        'description' => 'optional: css classes',
+                        'example_value' => ''
                     ],
-                    'icon'=>[
-                        'group'=>'content', 
-                        'description'=>'css class for an icon', 
-                        'example_value'=>'fa-regular fa-thumbs-up'
+                    'icon' => [
+                        'group' => 'content',
+                        'description' => 'css class for an icon',
+                        'example_value' => 'fa-regular fa-thumbs-up'
                     ],
-                    'text'=>[
-                        'group'=>'content', 
-                        'description'=>'short information text', 
-                        'example_value'=>'Likes'
+                    'text' => [
+                        'group' => 'content',
+                        'description' => 'short information text',
+                        'example_value' => 'Likes'
                     ],
-                    'number'=>[
-                        'group'=>'content', 
-                        'description'=>'a number to highlight', 
-                        'example_value'=>"41,410"
+                    'number' => [
+                        'group' => 'content',
+                        'description' => 'a number to highlight',
+                        'example_value' => "41,410"
                     ],
-                    'progressvalue'=>[
-                        'group'=>'content', 
-                        'description'=>'optional: progress value 0..100 to draw a progress bar', 
-                        'example_value'=>70
+                    'progressvalue' => [
+                        'group' => 'content',
+                        'description' => 'optional: progress value 0..100 to draw a progress bar',
+                        'example_value' => 70
                     ],
-                    'progresstext'=>[
-                        'group'=>'content', 
-                        'description'=>'optional: text below progress bar', 
-                        'example_value'=>'70% Increase in 30 Days'
+                    'progresstext' => [
+                        'group' => 'content',
+                        'description' => 'optional: text below progress bar',
+                        'example_value' => '70% Increase in 30 Days'
                     ]
                 ]
             ],
             // ------------------------------------------------------------
-            'smallbox'=>[
-                'label'=>'Small box',
-                'description'=>'Solid colored box to highlight a single value; optional with a link',
-                'method'=>'getSmallbox',
-        
-                'params'=>[
-                    'type'=>['select'=>$this->aPresets['type'],     'example_value'=>'info'],
-                    'shadow'=>['select'=>$this->aPresets['shadow'], 'example_value'=>''],
-                    'class'       => [
-                        'group'=>'styling', 
-                        'description'=>'optional: css classes', 
-                        'example_value'=>''
+            'smallbox' => [
+                'label' => 'Small box',
+                'description' => 'Solid colored box to highlight a single value; optional with a link',
+                'method' => 'getSmallbox',
+
+                'params' => [
+                    'type' => ['select' => $this->aPresets['type'], 'example_value' => 'info'],
+                    'shadow' => ['select' => $this->aPresets['shadow'], 'example_value' => ''],
+                    'class' => [
+                        'group' => 'styling',
+                        'description' => 'optional: css classes',
+                        'example_value' => ''
                     ],
-                    'icon'=>['group'=>'content', 'description'=>'css class for an icon', 'example_value'=>'fa-solid fa-shopping-cart'],
-        
-                    'text'=>['group'=>'content', 'description'=>'short information text', 'example_value'=>'New orders'],
-                    'number'=>['group'=>'content', 'description'=>'a number to highlight', 'example_value'=>"150"],
-                    'url'=>['group'=>'content', 'description'=>'optional: url to set a link on the bottom', 'example_value'=>'#'],
-                    'linktext'=>['group'=>'content', 'description'=>'used if a url was given: linked text', 'example_value'=>'More info']
+                    'icon' => ['group' => 'content', 'description' => 'css class for an icon', 'example_value' => 'fa-solid fa-shopping-cart'],
+
+                    'text' => ['group' => 'content', 'description' => 'short information text', 'example_value' => 'New orders'],
+                    'number' => ['group' => 'content', 'description' => 'a number to highlight', 'example_value' => "150"],
+                    'url' => ['group' => 'content', 'description' => 'optional: url to set a link on the bottom', 'example_value' => '#'],
+                    'linktext' => ['group' => 'content', 'description' => 'used if a url was given: linked text', 'example_value' => 'More info']
                 ]
             ],
             // ------------------------------------------------------------
-            'input'=>[
-                'label'=>'Form: input',
-                'description'=>'Input form fiels',
-                'method'=>'getFormInput',
-        
-                'params'=>[
-                    'label'       => [
-                        'group'=>'styling', 
-                        'description'=>'label for the input field', 
-                        'example_value'=>'Enter something'
+            'input' => [
+                'label' => 'Form: input',
+                'description' => 'Input form fiels',
+                'method' => 'getFormInput',
+
+                'params' => [
+                    'label' => [
+                        'group' => 'styling',
+                        'description' => 'label for the input field',
+                        'example_value' => 'Enter something'
                     ],
-                    'type'=>['select'=> [
-                            'description'=>'type or input field',
-                            'group'=>'styling',
-                            'values'=>[
-                                'text'=>'text',
-                                'password'=>'password',
-                                'email'=>'email',
-                                'hidden'=>'hidden',
-
-                                'button'=>'button',
-                                'checkbox'=>'checkbox',
-                                'color'=>'color',
-                                'date'=>'date',
-                                'datetime-local'=>'datetime-local',
-                                'email'=>'email',
-                                'file'=>'file',
-                                'hidden'=>'hidden',
-                                'image'=>'image',
-                                'month'=>'month',
-                                'number'=>'number',
-                                'password'=>'password',
-                                'radio'=>'radio',
-                                'range'=>'range',
-                                'reset'=>'reset',
-                                'search'=>'search',
-                                'submit'=>'submit',
-                                'tel'=>'tel',
-                                'text'=>'text',
-                                'time'=>'time',
-                                'url'=>'url',
-                                'week'=>'week',
-                                ]
-                            ], 
-                            'example_value'=>'text'
+                    'type' => [
+                        'select' => [
+                            'description' => 'type or input field',
+                            'group' => 'styling',
+                            'values' => [
+
+                                'button' => 'button',
+                                'checkbox' => 'checkbox',
+                                'color' => 'color',
+                                'date' => 'date',
+                                'datetime-local' => 'datetime-local',
+                                'email' => 'email',
+                                'file' => 'file',
+                                'hidden' => 'hidden',
+                                'image' => 'image',
+                                'month' => 'month',
+                                'number' => 'number',
+                                'password' => 'password',
+                                'radio' => 'radio',
+                                'range' => 'range',
+                                'reset' => 'reset',
+                                'search' => 'search',
+                                'submit' => 'submit',
+                                'tel' => 'tel',
+                                'text' => 'text',
+                                'time' => 'time',
+                                'url' => 'url',
+                                'week' => 'week',
+                            ]
+                        ],
+                        'example_value' => 'text'
                     ],
-                    'class'       => [
-                        'group'=>'styling', 
-                        'description'=>'optional: css classes', 
-                        'example_value'=>'myclass'
+                    'class' => [
+                        'group' => 'styling',
+                        'description' => 'optional: css classes',
+                        'example_value' => 'myclass'
                     ],
-                    'prepend'       => [
-                        'group'=>'styling', 
-                        'description'=>'optional: content on input start', 
-                        'example_value'=>''
+                    'prepend' => [
+                        'group' => 'styling',
+                        'description' => 'optional: content on input start',
+                        'example_value' => ''
                     ],
-                    'append'       => [
-                        'group'=>'styling', 
-                        'description'=>'optional: content on input end', 
-                        'example_value'=>''
+                    'append' => [
+                        'group' => 'styling',
+                        'description' => 'optional: content on input end',
+                        'example_value' => ''
                     ],
-                    'name'       => [
-                        'group'=>'content', 
-                        'description'=>'name attribute', 
-                        'example_value'=>'firstname'
+                    'name' => [
+                        'group' => 'content',
+                        'description' => 'name attribute',
+                        'example_value' => 'firstname'
                     ],
                     'value' => [
-                        'group'=>'content', 
-                        'description'=>'Value', 
-                        'example_value'=>'Jack'
+                        'group' => 'content',
+                        'description' => 'Value',
+                        'example_value' => 'Jack'
                     ],
                 ]
             ],
             // ------------------------------------------------------------
-            'textarea'=>[
-                'label'=>'Form: textarea',
-                'description'=>'textarea or html editor',
-                'method'=>'getFormTextarea',
-        
-                'params'=>[
-                    'label'       => [
-                        'group'=>'styling', 
-                        'description'=>'label for the input field', 
-                        'example_value'=>'Enter text'
+            // WIP
+            'select' => [
+                'label' => 'Form: select',
+                'description' => 'Select box',
+                'method' => 'getFormSelect',
+
+                'params' => [
+                    'label' => [
+                        'group' => 'styling',
+                        'description' => 'label for the select field',
+                        'example_value' => 'Enter text'
+                    ],
+                    'bootstrap-select' => [
+                        'select' => [
+                            'description' => 'Enable bootstrap-select plugin',
+                            'group' => 'styling',
+                            'values' => [
+                                '0' => 'no',
+                                '1' => 'yes',
+                            ]
+                        ]
+                    ],
+                    'class' => [
+                        'group' => 'styling',
+                        'description' => 'optional: css classes',
+                        'example_value' => 'myclass'
+                    ],
+                    'options' => [
+                        'example_value' => [
+                            ["value" => "1", "label" => "one"],
+                            ["value" => "2", "label" => "two"],
+                            ["value" => "3", "label" => "three"],
+                        ],
+                    ]
+                ],
+            ],
+            // ------------------------------------------------------------
+            'textarea' => [
+                'label' => 'Form: textarea',
+                'description' => 'textarea or html editor',
+                'method' => 'getFormTextarea',
+
+                'params' => [
+                    'label' => [
+                        'group' => 'styling',
+                        'description' => 'label for the input field',
+                        'example_value' => 'Enter text'
                     ],
-                    'type'=>['select'=> [
-                            'description'=>'type or input field',
-                            'group'=>'styling',
-                            'values'=>[
-                                ''=>'text',
-                                'html'=>'html editor',
-                                ]
-                            ], 
+                    'type' => [
+                        'select' => [
+                            'description' => 'type or input field',
+                            'group' => 'styling',
+                            'values' => [
+                                '' => 'text',
+                                'html' => 'html editor',
+                            ]
+                        ],
                     ],
-                    'class'       => [
-                        'group'=>'styling', 
-                        'description'=>'optional: css classes', 
-                        'example_value'=>'myclass'
+                    'class' => [
+                        'group' => 'styling',
+                        'description' => 'optional: css classes',
+                        'example_value' => 'myclass'
                     ],
-                    'name'       => [
-                        'group'=>'content', 
-                        'description'=>'name attribute', 
-                        'example_value'=>'textdata'
+                    'name' => [
+                        'group' => 'content',
+                        'description' => 'name attribute',
+                        'example_value' => 'textdata'
                     ],
                     'value' => [
-                        'group'=>'content', 
-                        'description'=>'Value', 
-                        'example_value'=>'Here is some text...'
+                        'group' => 'content',
+                        'description' => 'Value',
+                        'example_value' => 'Here is some text...'
                     ],
                 ]
-            ],            
+            ],
         ];
     }
     /**
@@ -502,10 +555,12 @@ class renderadminlte {
      * @param  array   $aAttributes  array of its attributes
      * @param  string  $sContent     content between opening and closing tag
      * @param  bool    $bClosetag    flag: write a closing tag or not? default: true
+     * @return string
      */
-    protected function _tag($sTag, $aAttributes, $sContent=false, $bClosetag=true){
-        if ($sContent){
-            $aAttributes['label']=(isset($aAttributes['label']) ? $aAttributes['label'] : '') . $sContent;
+    protected function _tag(string $sTag, array $aAttributes, string $sContent = '', bool $bClosetag = true): string
+    {
+        if ($sContent) {
+            $aAttributes['label'] = (isset($aAttributes['label']) ? $aAttributes['label'] : '') . $sContent;
         }
         return $this->_oHtml->getTag($sTag, $aAttributes, $bClosetag);
     }
@@ -519,25 +574,29 @@ class renderadminlte {
      * render a page by using template 
      * @param  string  $stemplate  html template with placeholders
      * @param  array   $aReplace   key = what to replace .. value = new value
+     * @return string
      */
-    public function render($sTemplate, $aReplace){
+    public function render(string $sTemplate, array $aReplace): string
+    {
         return str_replace(
             array_keys($aReplace),
             array_values($aReplace),
             $sTemplate
         );
     }
-    
 
     /**
      * add a wrapper: wrap some content into a tag
      * 
+     * @param  string  $sTag      name of html tag
+     * @param  array   $aOptions  array of its attributes
      * @param  string  $sContent  html content inside
      * @return string
      */
-    public function addWrapper($sTag, $aOptions, $sContent){
-        $aOptions['label']=$sContent;
-        return $this->_tag($sTag, $aOptions)."\n";
+    public function addWrapper(string $sTag, array $aOptions, string $sContent): string
+    {
+        $aOptions['label'] = $sContent;
+        return $this->_tag($sTag, $aOptions) . PHP_EOL;
     }
 
     // ----------------------------------------------------------------------
@@ -546,7 +605,6 @@ class renderadminlte {
     // 
     // ----------------------------------------------------------------------
 
-
     /** 
      * get a single navigation item on top bar
      * 
@@ -554,80 +612,93 @@ class renderadminlte {
      * @param  integer  $iLevel  Menu level; 1=top bar; 2=pupup menu with subitems
      * @return string
      */
-    public function getNavItem($aLink, $iLevel=1){
+    public function getNavItem(array $aLink, int $iLevel = 1): string
+    {
         static $iCounter;
-        if(!isset($iCounter)){
-            $iCounter=0;
+        if (!isset($iCounter)) {
+            $iCounter = 0;
         }
-        
+
         switch ($aLink['label']) {
             // special menu entry: horizontal bar (label is "-")
-            case '-': return '<div class="dropdown-divider"></div>'; break;
+            case '-':
+                return '<div class="dropdown-divider"></div>';
+                break;
 
             // special menu entry: hamburger menu item (label is "=")
-            case '=': return '<li class="nav-item"><a class="nav-link" data-widget="pushmenu" href="#" role="button"><i class="fa-solid fa-bars"></i></a></li>'; break;
+            case '=':
+                return '<li class="nav-item"><a class="nav-link" data-widget="pushmenu" href="#" role="button"><i class="fas fa-bars"></i></a></li>';
+                break;
 
             // special menu entry: hamburger menu item (label is "|")
             // requires css: .navbar-nav li.divider{border-left: 1px solid rgba(0,0,0,0.2);}
-            case '|': return '<li class="divider"></li>'; break;
+            case '|':
+                return '<li class="divider"></li>';
+                break;
         }
 
-        $aChildren=isset($aLink['children']) && is_array($aLink['children']) && count($aLink['children']) ? $aLink['children'] : false;
+        $aChildren = isset($aLink['children']) && is_array($aLink['children']) && count($aLink['children']) ? $aLink['children'] : false;
 
-        $aLink['class']='nav-link'
-            . (isset($aLink['class']) ? ' '.$aLink['class'] : '')
+        $aLink['class'] = 'nav-link'
+            . (isset($aLink['class']) ? ' ' . $aLink['class'] : '')
             . ($aChildren ? ' dropdown-toggle' : '')
-            ;
-        if($aChildren){
+        ;
+        if ($aChildren) {
             $iCounter++;
-            $sNavId="navbarDropdown".$iCounter;
-            $aLink=array_merge($aLink,[
-                'id'=>$sNavId,
-                'role'=>"button",
-                'data-toggle'=>"dropdown",
-                'aria-haspopup'=>"true",
-                'aria-expanded'=>"false",
+            $sNavId = "navbarDropdown" . $iCounter;
+            $aLink = array_merge($aLink, [
+                'id' => $sNavId,
+                'role' => "button",
+                'data-toggle' => "dropdown",
+                'aria-haspopup' => "true",
+                'aria-expanded' => "false",
             ]);
             unset($aLink['children']); // remove from html attributes to draw
         }
-        $sReturn=$this->_tag('a', $aLink)."\n";
-        if($aChildren){ 
+        $sReturn = $this->_tag('a', $aLink) . "\n";
+        if ($aChildren) {
             $iLevel++;
-            $sReturn.=$this->addWrapper(
-                    'div', 
-                    [ 
-                        'aria-labelledby'=> $sNavId,
-                        'class'=>'dropdown-menu'
-                    ], 
-                    $this->getNavItems($aChildren, $iLevel)
-                )."\n";
+            $sReturn .= $this->addWrapper(
+                'div',
+                [
+                    'aria-labelledby' => $sNavId,
+                    'class' => 'dropdown-menu'
+                ],
+                $this->getNavItems($aChildren, $iLevel)
+            ) . "\n";
             $iLevel--;
         }
-   
-        if($iLevel==1){
-            $sLiClass='nav-item'.($aChildren ? ' dropdown' : '');
-            $sReturn=$this->addWrapper(
-                'li', 
-                ['class'=>$sLiClass],
-                $sReturn 
+
+        if ($iLevel == 1) {
+            $sLiClass = 'nav-item' . ($aChildren ? ' dropdown' : '');
+            $sReturn = $this->addWrapper(
+                'li',
+                ['class' => $sLiClass],
+                $sReturn
             );
         }
         return $sReturn;
     }
+
     /** 
-     * get a navigation item for top bar
+     * get html code for navigation on top bar
      * 
+     * @param array $aLinks  array of navigation items
+     * @param int   $iLevel  current navigation level; default: 1
+     * @return string|bool
      */
-    public function getNavItems($aLinks, $iLevel=1){
-        $sReturn='';
-        if (!$aLinks || !is_array($aLinks) || !count($aLinks)){
+    public function getNavItems(array $aLinks, int $iLevel = 1): string|bool
+    {
+        $sReturn = '';
+        if (!$aLinks || !is_array($aLinks) || !count($aLinks)) {
             return false;
         }
-        foreach($aLinks as $aLink){
-            $sReturn.=$this->getNavItem($aLink, $iLevel);
+        foreach ($aLinks as $aLink) {
+            $sReturn .= $this->getNavItem($aLink, $iLevel);
         }
         return $sReturn;
     }
+
     /**
      * get a top left navigation for a top navigation bar
      * 
@@ -649,69 +720,94 @@ class renderadminlte {
      *     .'</nav>'
      * </code>
      * 
-     * @param  array  $aNavitems   array of navigation items/ tree
-     * @param  array  $aUlOptions  array of html attrubutes for wrapping UL tag
+     * @param  array  $aNavItems        array of navigation items/ tree
+     * @param  array  $aUlOptions       array of html attrubutes for wrapping UL tag
+     * @param  array  $aNavItemsRight   array of html attrubutes for wrapping UL tag
+     * @param  array  $aUlOptionsRight  array of html attrubutes for wrapping UL tag
      * @return string
      */
-    public function getTopNavigation($aNavItems, $aUlOptions=false, $aNavItemsRight=[], $aUlOptionsRight=false){
+    public function getTopNavigation(array $aNavItems, array $aUlOptions = [], array $aNavItemsRight = [], array $aUlOptionsRight = []): string
+    {
         // array_unshift($aNavItems, ['class'=>'nav-link', 'data-widget'=>'pushmenu', 'href'=>'#', 'role'=>'button', 'label'=>'<i class="fa-solid fa-bars"></i>']);
-        $aUlOptLeft=$aUlOptions ? $aUlOptions : ['class'=>'navbar-nav'];
-        $aUlOptRight=$aUlOptionsRight ? $aUlOptionsRight : ['class'=>'navbar-nav ml-auto'];
+        $aUlOptLeft = count($aUlOptions) ? $aUlOptions : ['class' => 'navbar-nav'];
+        $aUlOptRight = count($aUlOptionsRight) ? $aUlOptionsRight : ['class' => 'navbar-nav ml-auto'];
         return $this->addWrapper('ul', $aUlOptLeft, $this->getNavItems($aNavItems))
-            .(count($aNavItemsRight)
+            . (count($aNavItemsRight)
                 ? $this->addWrapper('ul', $aUlOptRight, $this->getNavItems($aNavItemsRight))
                 : ''
             )
-            ;
+        ;
     }
 
     // ----------------------------------------------------------------------
 
 
     /** 
-     * get a navigation items for sidebar
+     * Get a navigation items for sidebar
+     * Links can be nested with the key "children".
+     * 
+     * Remark: for a horizontal line ($aLink['label']='-') this css is required
+     *   .nav-item hr{color: #505860; border-top: 1px solid; height: 1px; padding: 0; margin: 0; }
+     * 
      * @param  array  $aLinks  list of link items
+     * @return string|bool
      */
-    public function getNavi2Items($aLinks){
-        $sReturn='';
-        if (!$aLinks || !is_array($aLinks) || !count($aLinks)){
+    public function getNavi2Items(array $aLinks): string|bool
+    {
+        $sReturn = '';
+        if (!$aLinks || !is_array($aLinks) || !count($aLinks)) {
             return false;
         }
-        foreach($aLinks as $aLink){
+
+        foreach ($aLinks as $aLink) {
+
+            if ($aLink['label'] == '-') {
+                // TODO: draw a nice line
+                $sReturn .= '<li class="nav-item"><hr></li>';
+                continue;
+            }
             // to render active or open links: 
-            $aLink['class']='nav-link' . (isset($aLink['class']) ? ' '.$aLink['class'] : '');
-
-            $aChildren=isset($aLink['children']) ? $aLink['children'] : false;
-            $aLiClass='nav-item' . ($aChildren && strstr($aLink['class'], 'active') ? ' menu-open' : '');
-            $sSubmenu='';
-            if($aChildren){
-                unset($aLink['children']);                
-                $aLink['label'].='<i class="right fa-solid fa-angle-left"></i>';
-                $sSubmenu.=$this->getSidebarNavigation($aChildren, ['class'=>'nav nav-treeview']);
+            $aLink['class'] = 'nav-link' . (isset($aLink['class']) ? ' ' . $aLink['class'] : '');
+
+            $aChildren = isset($aLink['children']) ? $aLink['children'] : false;
+            $aLiClass = 'nav-item' . ($aChildren && strstr($aLink['class'], 'active') ? ' menu-open' : '');
+            $sSubmenu = '';
+            if ($aChildren) {
+                unset($aLink['children']);
+                $aLink['label'] .= '<i class="right fa-solid fa-angle-left"></i>';
+                $sSubmenu .= $this->getSidebarNavigation($aChildren, ['class' => 'nav nav-treeview']);
             }
-            $aLink['label']=$this->addWrapper('p', [], $aLink['label']);
-            $sReturn.=$this->addWrapper(
-                'li', ['class'=>$aLiClass],
-                $this->_tag('a', $aLink).$sSubmenu
-            )."\n";
+            $aLink['label'] = $this->addWrapper('p', [], $aLink['label']);
+            $sReturn .= $this->addWrapper(
+                'li',
+                ['class' => $aLiClass],
+                $this->_tag('a', $aLink) . $sSubmenu
+            ) . "\n";
         }
         return $sReturn;
     }
 
     /**
+     * get html code for sidebar navigation
      * 
+     * @param  array  $aNavItems   navigation item
+     * @param  array  $aUlOptions  aatributes for UL tag
+     * @param string
      */
-    public function getSidebarNavigation($aNavItems, $aUlOptions=[
-            'class'=>'nav nav-pills nav-sidebar flex-column nav-flat_ nav-child-indent',
-            'data-widget'=>'treeview',
-            'role'=>'menu',
-            'data-accordion'=>'false'
-            ])
-    {
+    public function getSidebarNavigation(
+        array $aNavItems,
+        array $aUlOptions = [
+            'class' => 'nav nav-pills nav-sidebar flex-column nav-flat_ nav-child-indent',
+            'data-widget' => 'treeview',
+            'role' => 'menu',
+            'data-accordion' => 'false'
+        ]
+    ): string {
         return $this->addWrapper(
-                'ul', $aUlOptions, 
-                $this->getNavi2Items($aNavItems)
-            )."\n";
+            'ul',
+            $aUlOptions,
+            $this->getNavi2Items($aNavItems)
+        ) . "\n";
     }
 
     // ----------------------------------------------------------------------
@@ -719,15 +815,16 @@ class renderadminlte {
     // PUBLIC FUNCTIONS :: CONTENT - BASIC FUNCTIONS
     // 
     // ----------------------------------------------------------------------
-    
+
     /**
      * add page row
      * 
      * @param  string  $sContent  html content inside
      * @return string
      */
-    public function addRow($sContent){
-        return $this->addWrapper('div', ['class'=>'row'], $sContent);
+    public function addRow(string $sContent): string
+    {
+        return $this->addWrapper('div', ['class' => 'row'], $sContent);
     }
 
     /**
@@ -738,8 +835,9 @@ class renderadminlte {
      * @param  string   $sFloat    css value for float attribute; default=false
      * @return string
      */
-    public function addCol($sContent, $iCols=6, $sFloat=false){
-        return $this->addWrapper('div', ['class'=>'col-md-'.$iCols, 'style'=>'float:'.$sFloat], $sContent);
+    public function addCol(string $sContent, int $iCols = 6, string $sFloat = ''): string
+    {
+        return $this->addWrapper('div', ['class' => 'col-md-' . $iCols, 'style' => 'float:' . $sFloat], $sContent);
     }
 
     // ----------------------------------------------------------------------
@@ -748,49 +846,52 @@ class renderadminlte {
     // 
     // ----------------------------------------------------------------------
 
-
     /**
      * get a list of all defined components that can be rendered
-     * @param  {bool}  $bSendData  flag: send including subkeys of the hash; default: false (keys only)
-     * @return {array}
+     * @param   bool  $bSendData  flag: send including subkeys of the hash; default: false (keys only)
+     * @return  array
      */
-    public function getComponents($bSendData=false){
+    public function getComponents(bool $bSendData = false): array
+    {
         return $bSendData
             ? $this->_aElements
             : array_keys($this->_aElements)
         ;
     }
+
     /**
      * get data of a component
-     * @param  {string}  $sComponent  id of the component
-     * @return {array}
+     * @param  string  $sComponent  id of the component
+     * @return array|bool
      */
-    public function getComponent($sComponent){
-        if(!isset($this->_aElements[$sComponent])){
+    public function getComponent(string $sComponent): array|bool
+    {
+        if (!isset($this->_aElements[$sComponent])) {
             return false;
         }
-        $aReturn=array_merge(['id'=>$sComponent], $this->_aElements[$sComponent]);
+        $aReturn = array_merge(['id' => $sComponent], $this->_aElements[$sComponent]);
         unset($aReturn['params']);
         return $aReturn;
     }
 
     /**
      * get parameter keys of a component
-     * @param  {string}  $sComponent  id of the component
-     * @param  {bool}    $bSendData  flag: send including subkeys of the hash; default: false (keys only)
-     * @return {array}
+     * @param  string  $sComponent  id of the component
+     * @param  bool    $bSendData   flag: send including subkeys of the hash; default: false (keys only)
+     * @return array|bool
      */
-    public function getComponentParamkeys($sComponent, $bSendData=false){
-        if(!isset($this->_aElements[$sComponent])){
+    public function getComponentParamkeys(string $sComponent, bool $bSendData = false)
+    {
+        if (!isset($this->_aElements[$sComponent])) {
             return false;
         }
-        $aKeys=array_keys($this->_aElements[$sComponent]['params']);
-        if(!$bSendData){
+        $aKeys = array_keys($this->_aElements[$sComponent]['params']);
+        if (!$bSendData) {
             return $aKeys;
         }
-        $aReturn=[];
-        foreach($aKeys as $sKey){
-            $aReturn[$sKey]=$this->getComponentParamkey($sComponent, $sKey);
+        $aReturn = [];
+        foreach ($aKeys as $sKey) {
+            $aReturn[$sKey] = $this->getComponentParamkey($sComponent, $sKey);
         }
         // $aReturn=$this->_aElements[$sComponent]['params'];
         return $aReturn;
@@ -798,34 +899,36 @@ class renderadminlte {
 
     /**
      * get information a parameter keys of a component
-     * @param  {string}  $sComponent  id of the component
-     * @param  {string}  $sKey        key in the options array
-     * @return {array}
+     * @param  string  $sComponent  id of the component
+     * @param  string  $sKey        key in the options array
+     * @return array|bool
      */
-    public function getComponentParamkey($sComponent, $sKey){
-        if(!isset($this->_aElements[$sComponent]['params'][$sKey])){
+    public function getComponentParamkey(string $sComponent, string $sKey): array|bool
+    {
+        if (!isset($this->_aElements[$sComponent]['params'][$sKey])) {
             return false;
         }
-        $aReturn=$this->_aElements[$sComponent]['params'][$sKey];
+        $aReturn = $this->_aElements[$sComponent]['params'][$sKey];
         // get description from a preset
-        if(!isset($aReturn['description']) && isset($aReturn['select']['description'])){
-            $aReturn['description']=$aReturn['select']['description'];
+        if (!isset($aReturn['description']) && isset($aReturn['select']['description'])) {
+            $aReturn['description'] = $aReturn['select']['description'];
         }
-        if(!isset($aReturn['group']) && isset($aReturn['select']['group'])){
-            $aReturn['group']=$aReturn['select']['group'];
+        if (!isset($aReturn['group']) && isset($aReturn['select']['group'])) {
+            $aReturn['group'] = $aReturn['select']['group'];
         }
         return $aReturn;
     }
 
     /**
      * get a flat list of valid parameters for a key in a component
-     * @param  {string}  $sComponent  id of the component
-     * @param  {string}  $sKey        key in the options array
-     * @return {array}
+     * @param  string  $sComponent  id of the component
+     * @param  string  $sKey        key in the options array
+     * @return array|bool
      */
-    public function getValidParamValues($sComponent, $sKey){
-        $aOptionkey=$this->getComponentParamkey($sComponent, $sKey);
-        if(!$aOptionkey || !isset($aOptionkey['select']['values'])){
+    public function getValidParamValues(string $sComponent, string $sKey): array|bool
+    {
+        $aOptionkey = $this->getComponentParamkey($sComponent, $sKey);
+        if (!$aOptionkey || !isset($aOptionkey['select']['values'])) {
             return false;
         }
         return array_keys($aOptionkey['select']['values']);
@@ -838,43 +941,49 @@ class renderadminlte {
      * helper: add a css value with prefix
      * this handles option keys in get[COMPONENT] methods
      * if a value is set then this function returns a space + prefix (param 2) + value
-     * @param  {string}  $sValue   option value
-     * @param  {string}  $sPrefix  prefix in front of css value
-     * @return {string}
+     * @param  string  $sValue   option value
+     * @param  string  $sPrefix  prefix in front of css value
+     * @return string
      */
-    protected function _addClassValue($sValue, $sPrefix=''){
-        return $sValue ? ' '.$sPrefix.$sValue : '';
+    protected function _addClassValue(string $sValue, string $sPrefix = ''): string
+    {
+        return $sValue ? ' ' . $sPrefix . $sValue : '';
     }
 
     /**
      * helper function for get[COMPONENTNAME] methods:
-     * ensure that all wanted keys exist in an array. Non existing keys will be added with value false
+     * ensure that all wanted keys exist in an array. Non existing keys will 
+     * be added with value false
+     * 
+     * @param string $sComponent    id of the component
+     * @param array  $aOptions      hash with keys for all options
      * @return array
      */
-    protected function _ensureOptions($sComponent, $aOptions=[]){
+    protected function _ensureOptions(string $sComponent, array $aOptions = []): array
+    {
 
-        $aParams=$this->getComponentParamkeys($sComponent, 0);
-        if(!$aParams){
-            $aOptions['_infos'][]="Warning: no definition was found for component $sComponent.";
+        $aParams = $this->getComponentParamkeys($sComponent, 0);
+        if (!$aParams) {
+            $aOptions['_infos'][] = "Warning: no definition was found for component $sComponent.";
             return $aOptions;
         }
-        foreach ($aParams as $sKey){
-            if(!isset($aOptions) || !isset($aOptions[$sKey])){
-                $aOptions[$sKey]=false;
-                if(!isset($aOptions['_infos'])){
-                    $aOptions['_infos']=[];
+        foreach ($aParams as $sKey) {
+            if (!isset($aOptions) || !isset($aOptions[$sKey])) {
+                $aOptions[$sKey] = false;
+                if (!isset($aOptions['_infos'])) {
+                    $aOptions['_infos'] = [];
                 }
-                $aOptions['_infos'][]="added missing key: $sKey";
+                $aOptions['_infos'][] = "added missing key: $sKey";
             }
 
             // $aParamdata
-            $aValidvalues=$this->getValidParamValues($sComponent, $sKey);
-            if($aValidvalues){
-                if(array_search($aOptions[$sKey], $aValidvalues)===false){
-                    echo "ERROR: [".$sComponent."] value &quot;".$aOptions[$sKey]."&quot; is not a valid for param key [".$sKey."]; it must be one of ".implode("|", $aValidvalues).'<br>';
+            $aValidvalues = $this->getValidParamValues($sComponent, $sKey);
+            if ($aValidvalues) {
+                if (array_search($aOptions[$sKey], $aValidvalues) === false) {
+                    echo "ERROR: [" . $sComponent . "] value &quot;" . $aOptions[$sKey] . "&quot; is not a valid for param key [" . $sKey . "]; it must be one of " . implode("|", $aValidvalues) . '<br>';
                 }
             }
-    
+
             // $this->_checkValue($sKey, $aOptions[$sKey], __METHOD__);
         }
         // echo '<pre>' . print_r($aOptions, 1) . '</pre>';
@@ -890,35 +999,36 @@ class renderadminlte {
      * return a alert box      
      * https://adminlte.io/themes/v3/pages/UI/general.html
      * 
-     * @param type $aOptions  hash with keys for all options
+     * @param array $aOptions  hash with keys for all options
      *                          - type - one of [none]|danger|info|primary|success|warning
      *                          - dismissible - if dismissible - one of true|false; default: false
      *                          - title
      *                          - text
      * @return string
      */
-    public function getAlert($aOptions){
-        $aOptions=$this->_ensureOptions('alert', $aOptions);
-        $aAlertIcons=[
-            'danger'=>'icon fa-solid fa-ban',
-            'info'=>'icon fa-solid fa-info',
-            'warning'=>'icon fa-solid fa-exclamation-triangle',
-            'success'=>'icon fa-solid fa-check',
+    public function getAlert(array $aOptions): string
+    {
+        $aOptions = $this->_ensureOptions('alert', $aOptions);
+        $aAlertIcons = [
+            'danger' => 'icon fa-solid fa-ban',
+            'info' => 'icon fa-solid fa-info',
+            'warning' => 'icon fa-solid fa-exclamation-triangle',
+            'success' => 'icon fa-solid fa-check',
         ];
 
-        $aElement=[
-            'class'=>'alert'
+        $aElement = [
+            'class' => 'alert'
                 . $this->_addClassValue($aOptions['type'], 'alert-')
                 . $this->_addClassValue($aOptions['dismissible'], 'alert-')
-                ,
-            'label'=>''
+            ,
+            'label' => ''
                 . ($aOptions['dismissible'] ? '<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' : '')
                 . $this->_tag('h5', [
-                    'label'=> ''
-                        .(isset($aAlertIcons[$aOptions['type']]) ? '<i class="'.$aAlertIcons[$aOptions['type']].'"></i> ' : '')
-                        .$aOptions['title']
-                    ])
-                .$aOptions['text']
+                    'label' => ''
+                        . (isset($aAlertIcons[$aOptions['type']]) ? '<i class="' . $aAlertIcons[$aOptions['type']] . '"></i> ' : '')
+                        . $aOptions['title']
+                ])
+                . $aOptions['text']
         ];
 
         return $this->_tag('div', $aElement);
@@ -940,20 +1050,22 @@ class renderadminlte {
      *                          - text    - visible text
      *                          - title   - optional: title attribute
      *                          - type    - one of [none]|danger|dark|info|primary|secondary|success|warning
+     * @return string
      */
-    public function getBadge($aOptions){
-        $aOptions=$this->_ensureOptions('badge', $aOptions);
-        $aElement=[];
-        $aElement['class']='badge'
-            . $this->_addClassValue($aOptions['class'],   '')
-            . $this->_addClassValue($aOptions['type'],    'badge-')
+    public function getBadge(array $aOptions): string
+    {
+        $aOptions = $this->_ensureOptions('badge', $aOptions);
+        $aElement = [];
+        $aElement['class'] = 'badge'
+            . $this->_addClassValue($aOptions['class'], '')
+            . $this->_addClassValue($aOptions['type'], 'badge-')
             . $this->_addClassValue($aOptions['bgcolor'], 'bg-')
-            ;
-        if ($aOptions['id']){
-            $aElement['id']=$aOptions['id'];
+        ;
+        if ($aOptions['id']) {
+            $aElement['id'] = $aOptions['id'];
         }
-        $aElement['title']=$aOptions['title'];
-        $aElement['label']=$aOptions['text'];
+        $aElement['title'] = $aOptions['title'];
+        $aElement['label'] = $aOptions['text'];
 
         return $this->_tag('span', $aElement);
     }
@@ -965,7 +1077,7 @@ class renderadminlte {
      * https://adminlte.io/themes/v3/pages/UI/buttons.html
      * 
      * <button type="button" class="btn btn-block btn-default">Default</button>
-     * @param type $aOptions  hash with keys for all options
+     * @param array $aOptions  hash with keys for all options
      *                          - type  - one of [none]|danger|dark|info|primary|secondary|success|warning
      *                          - size  - one of [none]|lg|sm|xs|flat
      *                          - class - any css class for customizing, eg. "disabled"
@@ -973,16 +1085,17 @@ class renderadminlte {
      *                          - text  - text on button
      * @return string
      */
-    public function getButton($aOptions){
-        $aOptions=$this->_ensureOptions('button', $aOptions);
-        $aElement=$aOptions;
-        $aElement['class']='btn'
-            . $this->_addClassValue($aOptions['type'],    'btn-')
-            . $this->_addClassValue($aOptions['size'],    'btn-')
-            . $this->_addClassValue($aOptions['class'],   '')
-            ;
-        $aElement['label']=$aOptions['text'] ? $aOptions['text'] : '&nbsp;';
-        foreach(['_infos', 'type', 'size', 'icon', 'text'] as $sDeleteKey){
+    public function getButton(array $aOptions): string
+    {
+        $aOptions = $this->_ensureOptions('button', $aOptions);
+        $aElement = $aOptions;
+        $aElement['class'] = 'btn'
+            . $this->_addClassValue($aOptions['type'], 'btn-')
+            . $this->_addClassValue($aOptions['size'], 'btn-')
+            . $this->_addClassValue($aOptions['class'], '')
+        ;
+        $aElement['label'] = $aOptions['text'] ? $aOptions['text'] : '&nbsp;';
+        foreach (['_infos', 'type', 'size', 'icon', 'text'] as $sDeleteKey) {
             unset($aElement[$sDeleteKey]);
         }
         return $this->_tag('button', $aElement);
@@ -992,7 +1105,7 @@ class renderadminlte {
      * get a callout (box with coloered left border; has type, title + text)
      * https://adminlte.io/themes/v3/pages/UI/general.html
      * 
-     * @param type $aOptions  hash with keys for all options
+     * @param array $aOptions  hash with keys for all options
      *                        >> styling
      *                          - type    - one of [none]|danger|dark|info|primary|secondary|success|warning
      *                          - class   - optional css class
@@ -1002,19 +1115,21 @@ class renderadminlte {
      *                          - text    - text: content of the card
      * @return string
      */
-    public function getCallout($aOptions){
-        $aOptions=$this->_ensureOptions('callout', $aOptions);
-        $sClass='callout'
-            . $this->_addClassValue($aOptions['type'],    'callout-')
-            . $this->_addClassValue($aOptions['class'],   '')
-            .($aOptions['shadow'] && isset($this->_aValueMappings['shadow'][$aOptions['shadow']]) 
-                ? ' '.$this->_aValueMappings['shadow'][$aOptions['shadow']] : '')
-            ;
+    public function getCallout(array $aOptions): string
+    {
+        $aOptions = $this->_ensureOptions('callout', $aOptions);
+        $sClass = 'callout'
+            . $this->_addClassValue($aOptions['type'], 'callout-')
+            . $this->_addClassValue($aOptions['class'], '')
+            . ($aOptions['shadow'] && isset($this->_aValueMappings['shadow'][$aOptions['shadow']])
+                ? ' ' . $this->_aValueMappings['shadow'][$aOptions['shadow']] : '')
+        ;
 
         return $this->addWrapper(
-            'div', ['class'=>$sClass],
-            ($aOptions['title'] ? $this->_tag('h5', ['label'=>$aOptions['title']]) : '')
-            .($aOptions['text'] ? $this->_tag('p', ['label'=>$aOptions['text']]) : '')
+            'div',
+            ['class' => $sClass],
+            ($aOptions['title'] ? $this->_tag('h5', ['label' => $aOptions['title']]) : '')
+            . ($aOptions['text'] ? $this->_tag('p', ['label' => $aOptions['text']]) : '')
         );
     }
 
@@ -1023,7 +1138,7 @@ class renderadminlte {
      * https://adminlte.io/docs/3.2/components/cards.html
      * https://adminlte.io/docs/3.2/javascript/card-widget.html
      * 
-     * @param type $aOptions  hash with keys for all options
+     * @param array $aOptions  hash with keys for all options
      *                        >> styling
      *                          - variant: "default"  - titlebar is colored
      *                                     "outline"  - a small stripe on top border is colored
@@ -1048,67 +1163,70 @@ class renderadminlte {
      *                          - footer  - text: footer of the card
      * @return string
      */
-    public function getCard($aOptions){
-        $aOptions=$this->_ensureOptions('card', $aOptions);
+    public function getCard(array $aOptions): string
+    {
+        $aOptions = $this->_ensureOptions('card', $aOptions);
         // css class prefixes based on "variant" value
-        $aVariants=[
-            'default'  => 'card-',
-            'outline'  => 'card-outline card-',
-            'solid'    => 'bg-',
+        $aVariants = [
+            'default' => 'card-',
+            'outline' => 'card-outline card-',
+            'solid' => 'bg-',
             'gradient' => 'bg-gradient-',
         ];
 
         // window states: css class and toolbar buttons to add
-        $aStates=[
-            'collapsed'=>[ 'class'=>'collapsed-card', 'tool'=>'tb-expand'],
-            'maximized'=>[ 'class'=>'maximized-card', 'tool'=>'tb-minimize'],
+        $aStates = [
+            'collapsed' => ['class' => 'collapsed-card', 'tool' => 'tb-expand'],
+            'maximized' => ['class' => 'maximized-card', 'tool' => 'tb-minimize'],
         ];
-        $aTools=[
-            'tb-collapse'=>'<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fa-solid fa-minus"></i></button>',
-            'tb-expand'=>'<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fa-solid fa-plus"></i></button>',
+        $aTools = [
+            'tb-collapse' => '<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fa-solid fa-minus"></i></button>',
+            'tb-expand' => '<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fa-solid fa-plus"></i></button>',
 
-            'tb-maximize'=>'<button type="button" class="btn btn-tool" data-card-widget="maximize"><i class="fa-solid fa-expand"></i></button>',
-            'tb-minimize'=>'<button type="button" class="btn btn-tool" data-card-widget="maximize"><i class="fa-solid fa-compress"></i></button>',
+            'tb-maximize' => '<button type="button" class="btn btn-tool" data-card-widget="maximize"><i class="fa-solid fa-expand"></i></button>',
+            'tb-minimize' => '<button type="button" class="btn btn-tool" data-card-widget="maximize"><i class="fa-solid fa-compress"></i></button>',
 
-            'tb-remove'=>'<button type="button" class="btn btn-tool" data-card-widget="remove"><i class="fa-solid fa-times"></i></button>',
+            'tb-remove' => '<button type="button" class="btn btn-tool" data-card-widget="remove"><i class="fa-solid fa-times"></i></button>',
         ];
 
         // print_r($aOptions);
 
-        $sVariantPrefix=isset($aVariants[$aOptions['variant']]) ? $aVariants[$aOptions['variant']] : $aVariants['default'];
-        $sClass='card'
-                . $this->_addClassValue($aOptions['type'],    $sVariantPrefix)
-                .($aOptions['shadow'] && isset($this->_aValueMappings['shadow'][$aOptions['shadow']]) 
-                    ? ' '.$this->_aValueMappings['shadow'][$aOptions['shadow']] : '')
-                . $this->_addClassValue($aOptions['class'],   '')
-                ;
+        $sVariantPrefix = isset($aVariants[$aOptions['variant']]) ? $aVariants[$aOptions['variant']] : $aVariants['default'];
+        $sClass = 'card'
+            . $this->_addClassValue($aOptions['type'], $sVariantPrefix)
+            . ($aOptions['shadow'] && isset($this->_aValueMappings['shadow'][$aOptions['shadow']])
+                ? ' ' . $this->_aValueMappings['shadow'][$aOptions['shadow']] : '')
+            . $this->_addClassValue($aOptions['class'], '')
+        ;
 
         // check window state
-        foreach($aStates as $sStatus=>$aStatus){
-            if($aOptions['state']===$sStatus){
-                $sClass.=' '.$aStatus['class'];
-                $aOptions[$aStatus['tool']]=1;
+        foreach ($aStates as $sStatus => $aStatus) {
+            if ($aOptions['state'] === $sStatus) {
+                $sClass .= ' ' . $aStatus['class'];
+                $aOptions[$aStatus['tool']] = 1;
             }
         }
 
         // add toolbar buttons - from given options or by window state
-        foreach($aTools as $sTool=>$sHtml){
-            $aOptions['tools'].=($aOptions[$sTool] ? $sHtml : '');
+        foreach ($aTools as $sTool => $sHtml) {
+            $aOptions['tools'] .= ($aOptions[$sTool] ? $sHtml : '');
         }
         // build parts of the card
-        $sCardHeader=$aOptions['title'] 
-            ? $this->addWrapper('div', ['class'=>'card-header'],
-            $this->_tag('h3', ['class'=>'card-title', 'label'=>$aOptions['title']])
-            . ($aOptions['tools'] ? $this->_tag('div', ['class'=>'card-tools', 'label'=>$aOptions['tools']]) : '')
+        $sCardHeader = $aOptions['title']
+            ? $this->addWrapper(
+                'div',
+                ['class' => 'card-header'],
+                $this->_tag('h3', ['class' => 'card-title', 'label' => $aOptions['title']])
+                . ($aOptions['tools'] ? $this->_tag('div', ['class' => 'card-tools', 'label' => $aOptions['tools']]) : '')
             )
             : ''
-            ;
+        ;
 
-        $sCardBody=$this->_tag('div', ['class'=>'card-body', 'label'=>$aOptions['text']]);
-        $sCardFooter=$aOptions['footer'] ? $this->_tag('div', ['class'=>'card-footer', 'label'=>$aOptions['footer']]) : '';
+        $sCardBody = $this->_tag('div', ['class' => 'card-body', 'label' => $aOptions['text']]);
+        $sCardFooter = $aOptions['footer'] ? $this->_tag('div', ['class' => 'card-footer', 'label' => $aOptions['footer']]) : '';
 
         // merge all
-        return $this->addWrapper('div', ['class'=>$sClass], $sCardHeader.$sCardBody.$sCardFooter);
+        return $this->addWrapper('div', ['class' => $sClass], $sCardHeader . $sCardBody . $sCardFooter);
     }
 
 
@@ -1117,7 +1235,7 @@ class renderadminlte {
      * A colored box with large icon, text and a value.
      * https://adminlte.io/docs/3.2/components/boxes.html
      * 
-     * @param type $aOptions  hash with keys for all options
+     * @param array $aOptions  hash with keys for all options
      *                        styling:
      *                          - type    - color of the box; one of [none]|danger|dark|info|primary|secondary|success|warning
      *                          - iconbg  - background color or type of icon; use it for default type (type="")
@@ -1131,39 +1249,44 @@ class renderadminlte {
      *                          - progresstext  - text below progress bar
      * @return string
      */
-    public function getInfobox($aOptions){
-        $aOptions=$this->_ensureOptions('infobox', $aOptions);
+    public function getInfobox(array $aOptions): string
+    {
+        $aOptions = $this->_ensureOptions('infobox', $aOptions);
 
         // print_r($aOptions);
-        $sClass='info-box'
-                . $this->_addClassValue($aOptions['type'],    'bg-')
-                . $this->_addClassValue($aOptions['class'],   '')
-                .($aOptions['shadow'] && isset($this->_aValueMappings['shadow'][$aOptions['shadow']]) 
-                    ? ' '.$this->_aValueMappings['shadow'][$aOptions['shadow']] : '')
-                ;
-        
+        $sClass = 'info-box'
+            . $this->_addClassValue($aOptions['type'], 'bg-')
+            . $this->_addClassValue($aOptions['class'], '')
+            . ($aOptions['shadow'] && isset($this->_aValueMappings['shadow'][$aOptions['shadow']])
+                ? ' ' . $this->_aValueMappings['shadow'][$aOptions['shadow']] : '')
+        ;
+
         // build parts
-        $sIcon=$aOptions['icon'] 
+        $sIcon = $aOptions['icon']
             ? $this->addWrapper("span", [
-                    'class'=>'info-box-icon'.($aOptions['iconbg'] ? ' bg-'.$aOptions['iconbg'] : '')
-                ], $this->_tag('i',['class'=>$aOptions['icon']])) 
+                'class' => 'info-box-icon' . ($aOptions['iconbg'] ? ' bg-' . $aOptions['iconbg'] : '')
+            ], $this->_tag('i', ['class' => $aOptions['icon']]))
             : ''
-            ;
-        $sContent=$this->addWrapper("div", ['class'=>'info-box-content'],
+        ;
+        $sContent = $this->addWrapper(
+            "div",
+            ['class' => 'info-box-content'],
             ''
-            . ($aOptions['text']   ? $this->_tag('span', ['class'=>'info-box-text',   'label'=>$aOptions['text']])   : '')
-            . ($aOptions['number'] ? $this->_tag('span', ['class'=>'info-box-number', 'label'=>$aOptions['number']]) : '')
-            . ($aOptions['progressvalue']!==false && $aOptions['progressvalue']!=='' 
-                ? $this->addWrapper('div', ['class'=>'progress'],
-                        $this->_tag('div', ['class'=>'progress-bar'. ($aOptions['iconbg'] ? ' bg-'.$aOptions['iconbg'] : ''), 'style'=>'width: '.(int)$aOptions['progressvalue'].'%' ]) 
-                    )
-                    . ($aOptions['progresstext'] ? $this->_tag('span', ['class'=>'progress-description', 'label'=>$aOptions['progresstext']]) : '' )
-                : ''    
-                ) 
+            . ($aOptions['text'] ? $this->_tag('span', ['class' => 'info-box-text', 'label' => $aOptions['text']]) : '')
+            . ($aOptions['number'] ? $this->_tag('span', ['class' => 'info-box-number', 'label' => $aOptions['number']]) : '')
+            . ($aOptions['progressvalue'] !== false && $aOptions['progressvalue'] !== ''
+                ? $this->addWrapper(
+                    'div',
+                    ['class' => 'progress'],
+                    $this->_tag('div', ['class' => 'progress-bar' . ($aOptions['iconbg'] ? ' bg-' . $aOptions['iconbg'] : ''), 'style' => 'width: ' . (int) $aOptions['progressvalue'] . '%'])
+                )
+                . ($aOptions['progresstext'] ? $this->_tag('span', ['class' => 'progress-description', 'label' => $aOptions['progresstext']]) : '')
+                : ''
+            )
         );
 
         // merge all
-        return $this->_tag('div', ['class'=>$sClass], $sIcon.$sContent);
+        return $this->_tag('div', ['class' => $sClass], $sIcon . $sContent);
     }
 
 
@@ -1173,7 +1296,7 @@ class renderadminlte {
      * https://adminlte.io/docs/3.2/components/boxes.html
      * https://adminlte.io/themes/v3/pages/widgets.html
      * 
-     * @param type $aOptions  hash with keys for all options
+     * @param array $aOptions  hash with keys for all options
      *                        styling:
      *                          - type    - color of the box; one of [none]|danger|dark|info|primary|secondary|success|warning
      *                          - shadow  - size of shadow; one of [none] (=default: between small and regular)|none|small|regular|large 
@@ -1185,42 +1308,50 @@ class renderadminlte {
      *                          - linktext- text below progress bar
      * @return string
      */
-    public function getSmallbox($aOptions){
-        $aOptions=$this->_ensureOptions('smallbox', $aOptions);
+    public function getSmallbox(array $aOptions): string
+    {
+        $aOptions = $this->_ensureOptions('smallbox', $aOptions);
         // print_r($aOptions);
-        $sClass='small-box'
-                . $this->_addClassValue($aOptions['type'],    'bg-')
-                . $this->_addClassValue($aOptions['class'],   '')
-                .($aOptions['shadow'] && isset($this->_aValueMappings['shadow'][$aOptions['shadow']]) 
-                    ? ' '.$this->_aValueMappings['shadow'][$aOptions['shadow']] : '')
-                ;
-        
+        $sClass = 'small-box'
+            . $this->_addClassValue($aOptions['type'], 'bg-')
+            . $this->_addClassValue($aOptions['class'], '')
+            . ($aOptions['shadow'] && isset($this->_aValueMappings['shadow'][$aOptions['shadow']])
+                ? ' ' . $this->_aValueMappings['shadow'][$aOptions['shadow']] : '')
+        ;
+
         // build parts
-        $sContent=$this->addWrapper("div", ['class'=>'inner'],
+        $sContent = $this->addWrapper(
+            "div",
+            ['class' => 'inner'],
             ''
-            . ($aOptions['number'] ? $this->_tag('h3', ['label'=>$aOptions['number']]) : '')
-            . ($aOptions['text']   ? $this->_tag('p', ['class'=>'info-box-text',   'label'=>$aOptions['text']])   : '')
+            . ($aOptions['number'] ? $this->_tag('h3', ['label' => $aOptions['number']]) : '')
+            . ($aOptions['text'] ? $this->_tag('p', ['class' => 'info-box-text', 'label' => $aOptions['text']]) : '')
         );
-        $sIcon=$aOptions['icon'] 
-            ? $this->addWrapper("div", ['class'=>'icon'], 
-                $this->_tag('i',['class'=>$aOptions['icon']])) 
+        $sIcon = $aOptions['icon']
+            ? $this->addWrapper(
+                "div",
+                ['class' => 'icon'],
+                $this->_tag('i', ['class' => $aOptions['icon']])
+            )
             : ''
-            ;
-        $sFooter=($aOptions['url']
-            ? $this->addWrapper("a", [
-                'class'=>'small-box-footer',
-                'href'=>$aOptions['url'],
+        ;
+        $sFooter = ($aOptions['url']
+            ? $this->addWrapper(
+                "a",
+                [
+                    'class' => 'small-box-footer',
+                    'href' => $aOptions['url'],
                 ],
                 ''
                 . ($aOptions['linktext'] ? $aOptions['linktext'] : $aOptions['url'])
                 . ' '
-                . $this->_tag('i',['class'=>'fa-solid fa-arrow-circle-right'])
+                . $this->_tag('i', ['class' => 'fa-solid fa-arrow-circle-right'])
             )
             : ''
         );
 
         // merge all
-        return $this->_tag('div', ['class'=>$sClass], $sContent.$sIcon.$sFooter);
+        return $this->_tag('div', ['class' => $sClass], $sContent . $sIcon . $sFooter);
     }
 
     // ----------------------------------------------------------------------
@@ -1230,120 +1361,229 @@ class renderadminlte {
     // ----------------------------------------------------------------------
 
 
-    public function getHorizontalFormElement($sInput, $sLabel=false, $sId=false){
+    /**
+     * Generates a horizontal form element with a label, input, and optional hint.
+     *
+     * @param string $sInput The HTML input element to be rendered.
+     * @param string $sLabel The label for the input element.
+     * @param string $sId The ID attribute for the label and input elements.
+     * @param string $sHint An optional hint to be displayed below the input element.
+     * @return string The generated HTML for the horizontal form element.
+     */
+    public function getHorizontalFormElement(string $sInput, string $sLabel = '', string $sId = '', string $sHint=''): string
+    {
         return '<div class="form-group row">'
-                . '<label for="' . $sId . '" class="col-sm-2 col-form-label">' . $sLabel . '</label>'
-                . '<div class="col-sm-10">'.$sInput.'</div>'
-              . '</div>'
-              ;
+            . '<label for="' . $sId . '" class="col-sm-2 col-form-label">' . $sLabel . '</label>'
+            . '<div class="col-sm-10">'
+            . ($sHint 
+                ? '<div class="text-navy hint">' . $sHint . '</div>' 
+                : '')
+            . $sInput
+            . '</div>'
+            . '</div>'
+        ;
     }
 
     /**
      * return a text input field:
      * https://adminlte.io/themes/v3/pages/forms/general.html
      * 
-     * @param type $aOptions  hash with keys for all options
+     * @param array $aOptions  hash with keys for all options
      *                        styling:
      *                          - type    - field type: text, email, password, hidden and all other html 5 input types
-      *                        content
+     *                        content
      *                          - label   - label tag
      *                          - name    - name attribute for sending form
      *                          - value   - value in field
+     *                        more:
+     *                          - hint    - hint to be displayed above the field
+     *                                      If not set, no hint is displayed.
+     *                                      css for ".row .hint" to customize look and feel
      * @return string
      */
-    public function GetFormInput($aOptions){
+    public function GetFormInput(array $aOptions): string
+    {
         // $aOptions=$this->_ensureOptions('input', $aOptions);
-        $aElement=$aOptions;
-        $aElement['class']=''
+        $aElement = $aOptions;
+        $aElement['class'] = ''
             . 'form-control '
-            . (isset($aOptions['class']) ? $aOptions['class']: '')
-            ;
-        $sFormid=(isset($aOptions['id']) 
-            ? $aOptions['id'] 
-            : (isset($aOptions['name']) ? $aOptions['name'] : 'field' ).'-'.md5(microtime(true))
+            . (isset($aOptions['class']) ? $aOptions['class'] : '')
+        ;
+        $sFormid = (isset($aOptions['id'])
+            ? $aOptions['id']
+            : (isset($aOptions['name']) ? $aOptions['name'] : 'field') . '-' . md5(microtime(true))
         );
+        $aElement['id'] = $sFormid;
 
-        $sLabel=isset($aOptions['label']) ? $aOptions['label'] : '';
-        $sPrepend='';
-        $sAppend='';
+        $sLabel = isset($aOptions['label']) ? $aOptions['label'] : '';
+        $sHint = isset($aOptions['hint']) ? $aOptions['hint'] : '';
+        $sPrepend = '';
+        $sAppend = '';
 
 
-        if(isset($aOptions['prepend']) && $aOptions['prepend']){
-            $sWrapperclass='input-group';
-            $sPrepend=$this->_tag('div',[ 'class'=>'input-group-prepend'],
-                $this->_tag('span', ['class'=>'input-group-text'] , $aOptions['prepend'])
+        if (isset($aOptions['prepend']) && $aOptions['prepend']) {
+            $sWrapperclass = 'input-group';
+            $sPrepend = $this->_tag(
+                'div',
+                ['class' => 'input-group-prepend'],
+                $this->_tag('span', ['class' => 'input-group-text'], $aOptions['prepend'])
             );
         }
-        if(isset($aOptions['append']) && $aOptions['append']){
-            $sWrapperclass='input-group';
-            $sAppend=$this->_tag('div',[ 'class'=>'input-group-append'],
-                $this->_tag('span', ['class'=>'input-group-text'] , $aOptions['append'])
+        if (isset($aOptions['append']) && $aOptions['append']) {
+            $sWrapperclass = 'input-group';
+            $sAppend = $this->_tag(
+                'div',
+                ['class' => 'input-group-append'],
+                $this->_tag('span', ['class' => 'input-group-text'], $aOptions['append'])
             );
-        }  
+        }
 
-        $aElement['id']=$sFormid;
-        foreach(['_infos', 'label', 'append', 'prepend', 'debug'] as $sDeleteKey){
-            if(isset($aElement[$sDeleteKey])){
+        foreach (['_infos', 'label', 'append', 'prepend', 'debug'] as $sDeleteKey) {
+            if (isset($aElement[$sDeleteKey])) {
                 unset($aElement[$sDeleteKey]);
             }
         }
 
         // return data
 
-        switch($aElement['type']){
+        switch ($aElement['type']) {
             case 'checkbox':
             case 'radio':
-                $aElement['class']=str_replace('form-control ', 'form-check-input', $aElement['class']);
-                return $this->_tag('div' , ['class'=>'form-check'],
-                    $this->_tag('input', $aElement, '', false).$this->_tag('label', ['for'=>$sFormid, 'label'=>$sLabel ], '')
+                $aElement['class'] = str_replace('form-control ', 'form-check-input', $aElement['class']);
+                $aElement['title'] = $aElement['title'] ?? $sHint;
+                return $this->_tag(
+                    'div',
+                    ['class' => 'form-check'],
+                    $this->_tag('input', $aElement, '', false) . $this->_tag('label', ['for' => $sFormid, 'label' => $sLabel], '')
                 );
                 break;
             case 'hidden':
+                $aElement['title'] = $aElement['title'] ?? $sHint;
                 return $this->_tag('input', $aElement, '', false);
                 break;
-            default: return $this->getHorizontalFormElement(
-                $sPrepend.$this->_tag('input', $aElement, '', false).$sAppend, 
-                $sLabel, 
-                $sFormid
-            );
+            default:
+                return $this->getHorizontalFormElement(
+                    $sPrepend . $this->_tag('input', $aElement, '', false) . $sAppend,
+                    $sLabel,
+                    $sFormid,
+                    $sHint
+                );
         }
     }
 
     /**
-     * return a textare field .. or html editor using summernote
-     * @param type $aOptions  hash with keys for all options
+     * return a textarea field .. or html editor using summernote
+     * @param array $aOptions  hash with keys for all options
      *                        styling:
      *                          - type    - field type: [none]|html
      *                        content
      *                          - label   - label tag
      *                          - name    - name attribute for sending form
      *                          - value   - value in 
+     *                        more:
+     *                          - hint    - hint to be displayed above the field
+     *                                      If not set, no hint is displayed.
+     *                                      css for ".row .hint" to customize look and feel
      * @return string
      */
-    public function getFormTextarea($aOptions){
+    public function getFormTextarea(array $aOptions): string
+    {
         // $aOptions=$this->_ensureOptions('textarea', $aOptions);
-        $aElement=$aOptions;
-        $aElement['class']=''
+        $aElement = $aOptions;
+        $aElement['class'] = ''
             . 'form-control '
-            . ((isset($aOptions['type']) && $aOptions['type']=='html' )? 'summernote ': '')
-            . (isset($aOptions['class']) ? $aOptions['class']: '')
+            . ((isset($aOptions['type']) && $aOptions['type'] == 'html') ? 'summernote ' : '')
+            . (isset($aOptions['class']) ? $aOptions['class'] : '')
         ;
-        
-        $sLabel=isset($aOptions['label']) ? $aOptions['label'] : '';
-        $sFormid=(isset($aOptions['id']) 
-            ? $aOptions['id'] 
-            : (isset($aOptions['name']) ? $aOptions['name'] : 'field' ).'-'.md5(microtime(true))
+        $sFormid = (isset($aOptions['id'])
+            ? $aOptions['id']
+            : (isset($aOptions['name']) ? $aOptions['name'] : 'field') . '-' . md5(microtime(true))
         );
-        $value=isset($aOptions['value']) ? $aOptions['value']: '';
-        foreach(['_infos', 'label', 'debug','type', 'value'] as $sDeleteKey){
-            if(isset($aElement[$sDeleteKey])){
+        $sLabel = isset($aOptions['label']) ? $aOptions['label'] : '';
+        $sHint = isset($aOptions['hint']) ? $aOptions['hint'] : '';
+        $aElement['id'] = $sFormid;
+
+        $value = isset($aOptions['value']) ? $aOptions['value'] : '';
+
+        foreach (['_infos', 'label', 'debug', 'type', 'value'] as $sDeleteKey) {
+            if (isset($aElement[$sDeleteKey])) {
                 unset($aElement[$sDeleteKey]);
             }
         }
         return $this->getHorizontalFormElement(
-            $this->_tag('textarea', $aElement, $value), 
-            $sLabel, 
-            $sFormid
+            $this->_tag('textarea', $aElement, $value),
+            $sLabel,
+            $sFormid,
+            $sHint
+        );
+
+    }
+
+    /**
+     * return a select box field
+     * @param array $aOptions  hash with keys for all options
+     *                        option fields
+     *                          - options - array of options with keys per item:
+     *                              - value   - value in the option
+     *                              - label   - visible text in the option
+     *                              other keys are attributes in the option
+     *                        styling:
+     *                          - bootstrap-select  - set true to enable select
+     *                                      box with bootstrap-select and
+     *                                      live search
+     *                          - class   - css class
+     *                        select tag
+     *                          - label   - label tag
+     *                          - name    - name attribute for sending form
+     *                          other keys are attributes in the select
+     *                        more:
+     *                          - hint    - hint to be displayed above the field
+     *                                      If not set, no hint is displayed.
+     *                                      css for ".row .hint" to customize look and feel
+     * @return string
+     */
+    public function getFormSelect(array $aOptions): string
+    {
+        $aElement = $aOptions;
+        $aElement['class'] = ''
+            . 'form-control '
+            . (isset($aOptions['class']) ? $aOptions['class'] . ' ' : '')
+            . (isset($aOptions['bootstrap-select']) ? 'selectpicker ' : '') //$aOptions
+        ;
+        if (isset($aOptions['bootstrap-select']) && $aOptions['bootstrap-select']) {
+            $aElement['data-live-search'] = "true";
+        }
+
+        $sFormid = (isset($aOptions['id'])
+
+            ? $aOptions['id']
+            : (isset($aOptions['name']) ? $aOptions['name'] : 'field') . '-' . md5(microtime(true))
+        );
+        $aElement['id'] = $sFormid;
+        $sLabel = isset($aOptions['label']) ? $aOptions['label'] : '';
+        $sHint = isset($aOptions['hint']) ? $aOptions['hint'] : '';
+
+        $sOptionTags = '';
+        foreach ($aOptions['options'] as $aField) {
+            $optionText = $aField['label'];
+            unset($aField['label']);
+            $sOptionTags .= $this->_tag('option', $aField, $optionText) . "\n";
+        }
+
+        foreach (['_infos', 'label', 'debug', 'type', 'value', 'options'] as $sDeleteKey) {
+            if (isset($aElement[$sDeleteKey])) {
+                unset($aElement[$sDeleteKey]);
+            }
+        }
+        return $this->getHorizontalFormElement(
+            $this->_tag(
+                'div',
+                ['class' => 'form-group'],
+                $this->_tag('select', $aElement, $sOptionTags)
+            ),
+            $sLabel,
+            $sFormid,
+            $sHint
         );
 
     }
@@ -1358,55 +1598,62 @@ class renderadminlte {
      * return a box with tabbed content
      * @param array $aOptions  hash with keys for all options
      *                           - tabs {array} key=tab label; value=content
-     * @retunr string|array
+     * @param bool  $asArray   optional flag: return hash with keys or as string
+     * @retunr bool|string|array
      */
-    public function getTabbedContent($aOptions, $asArray=false){
+    public function getTabbedContent(array $aOptions, bool $asArray = false): bool|string|array
+    {
         static $iTabCounter;
-        if( ! isset($aOptions['tabs']) || ! is_array($aOptions['tabs']) ){
+        if (!isset($aOptions['tabs']) || !is_array($aOptions['tabs'])) {
             return false;
         }
-        if (!isset($iTabCounter)){
-            $iTabCounter=1;
+        if (!isset($iTabCounter)) {
+            $iTabCounter = 1;
         } else {
-            $iTabCounter++;            
+            $iTabCounter++;
         }
 
-        $id='tab-content-'.$iTabCounter;
-        $iCounter=0;
+        $id = 'tab-content-' . $iTabCounter;
+        $iCounter = 0;
 
-        $sTabs='';
-        $sContent='';
-        foreach($aOptions['tabs'] as $sLabel => $sTabContent){
+        $sTabs = '';
+        $sContent = '';
+        foreach ($aOptions['tabs'] as $sLabel => $sTabContent) {
             $iCounter++;
-            $sTabId=$id.'-tabitem-'.$iCounter.'-tab';
-            $sContentId=$id.'-tabitem-'.$iCounter.'-content';
-
-            $sTabs.=$this->_tag('li', ['class'=>'nav-item'], 
-                $this->_tag('a', [
-                    'class'=>'nav-link'.($iCounter==1 ? ' active' : '' ),
-                    'id'=>$sTabId,
-                    'data-toggle'=>'tab',
-                    'href'=>'#'.$sContentId,
-                    'role'=>'tab',
-                    'aria-controls'=>'custom-tabs-one-profile',
-                    'aria-selected'=>($iCounter==1 ? true : false ), 
-                    ], 
-                    $sLabel)
+            $sTabId = $id . '-tabitem-' . $iCounter . '-tab';
+            $sContentId = $id . '-tabitem-' . $iCounter . '-content';
+
+            $sTabs .= $this->_tag(
+                'li',
+                ['class' => 'nav-item'],
+                $this->_tag(
+                    'a',
+                    [
+                        'class' => 'nav-link' . ($iCounter == 1 ? ' active' : ''),
+                        'id' => $sTabId,
+                        'data-toggle' => 'tab',
+                        'href' => '#' . $sContentId,
+                        'role' => 'tab',
+                        'aria-controls' => 'custom-tabs-one-profile',
+                        'aria-selected' => ($iCounter == 1 ? true : false),
+                    ],
+                    $sLabel
+                )
             );
-            $sContent.=$this->_tag('div', [
-                'class'=>'tab-pane fade'.($iCounter==1 ? ' active show' : ''), 
-                'id'=>$sContentId,
-                'role'=>'tabpanel',
-                'aria-labelledby'=>$sTabId,
-                ], $sTabContent);
+            $sContent .= $this->_tag('div', [
+                'class' => 'tab-pane fade' . ($iCounter == 1 ? ' active show' : ''),
+                'id' => $sContentId,
+                'role' => 'tabpanel',
+                'aria-labelledby' => $sTabId,
+            ], $sTabContent);
         }
-        $sTabs=$this->_tag('ul', ['class'=>'nav nav-tabs', 'role'=>'tablist'], $sTabs);
-        $sContent=$this->_tag('div', ['class'=>'tab-content'], $sContent);
+        $sTabs = $this->_tag('ul', ['class' => 'nav nav-tabs', 'role' => 'tablist'], $sTabs);
+        $sContent = $this->_tag('div', ['class' => 'tab-content'], $sContent);
 
         return $asArray
-            ? ['tabs'=>$sTabs, 'content'=>$sContent]
+            ? ['tabs' => $sTabs, 'content' => $sContent]
             : $sTabs . $sContent
-            ;
+        ;
     }
 
 }
diff --git a/public_html/deployment/classes/rollout.interface.php b/public_html/deployment/classes/rollout.interface.php
index 5b973b933d54f4dcb96bf4554cfb99827c6df57e..888e14edbf331457600c632425478a456c5756f6 100644
--- a/public_html/deployment/classes/rollout.interface.php
+++ b/public_html/deployment/classes/rollout.interface.php
@@ -3,71 +3,101 @@
  * INTERFACE for rollout plugins
  * 
  * @author hahn
+ * 
+ * Axel <axel.hahn@unibe.ch>
+ * 2024-08-29  Axel php8 only; added variable types
  */
-interface iRolloutplugin {
+interface iRolloutplugin
+{
 
     // ----------------------------------------------------------------------
     // VERIFY
     // ----------------------------------------------------------------------
-    
+
     /**
-     * check requirements if the plugin could work
+     * Get an array with shell commands to check requirements if the plugin
+     * can work
+     * 
+     * @return array
      */
-    public function checkRequirements();
-    
+    public function checkRequirements(): array;
+
     /**
-     * check access to a deploy target
+     * Get an array with shell commands to check access to a deploy target
+     * 
+     * @return array
      */
-    public function checkConnectionToTarget();
+    public function checkConnectionToTarget(): array;
 
     // ----------------------------------------------------------------------
     // SETTER
     // ----------------------------------------------------------------------
-    
+
     // ----------------------------------------------------------------------
     // GETTER
     // ----------------------------------------------------------------------
 
     /**
-     * get configuration for the project .. or more specifi for a given phase
+     * Get configuration array for the project .. or more specific for a given phase
+     * 
      * @param  string   $sPhase
      * @param  boolean  $bMask   Flag for public output; if true then mask your secrets
      * @return array
      */
-    public function getConfig($sPhase=false, $bMask=false);
-    
+    public function getConfig(string $sPhase = '', bool $bMask = false): array;
+
     /**
-     * get an array with shell commands to execute
+     * Get an array with shell commands to execute for deployment of built file
+     * 
      * @param  string   $sPhase
      * @param  boolean  $bMask   Flag for public output; if true then mask your secrets
      * @return array
      */
-    public function getDeployCommands($sPhase, $bMask=false);
+    public function getDeployCommands(string $sPhase, bool $bMask = false): array;
 
     /**
-     * get name of plugin as string ... language specific
+     * Get name of plugin as string ... language specific
+     * @return string
      */
-    public function getName();
-    
+    public function getName(): string;
+
     /**
-     * get description of plugin as string ... language specific
+     * Get description of plugin as string ... language specific
+     * @return string
      */
-    public function getDescription();
-    
+    public function getDescription(): string;
+
     /**
-     * get array of data in info.js
+     * Get array of data in info.js
+     * @return array
      */
-    public function getPluginInfos();
+    public function getPluginInfos(): array;
+
     // ----------------------------------------------------------------------
     // RENDERER
     // ----------------------------------------------------------------------
-    public function renderFormdata4Project();
-    public function renderFormdata4Phase($sPhase);
+
+    /**
+     * form renderer: show 
+     * - formvars for project OR
+     * - a single message that no configuration items exists
+     * 
+     * @return string
+     */
+    public function renderFormdata4Project(): string;
+
+    /**
+     * form renderer: show configuration for a given phase
+     
+     * @param string $sPhase  phaese; one of preview|stage|live
+     * @return string
+     */
+    public function renderFormdata4Phase(string $sPhase): string;
 
     // ----------------------------------------------------------------------
     // ACTIONS
     // ----------------------------------------------------------------------
 
-    
+
 }
 
diff --git a/public_html/deployment/classes/rollout_base.class.php b/public_html/deployment/classes/rollout_base.class.php
index c8b1b214131f65b9f24416041bb7e11bfcc835ba..fde105372f91d0e34784af8476cfecc1d769b5dd 100644
--- a/public_html/deployment/classes/rollout_base.class.php
+++ b/public_html/deployment/classes/rollout_base.class.php
@@ -1,15 +1,19 @@
 <?php
 require_once 'rollout.interface.php';
-require_once __DIR__.'/../../vendor/axelhahn/ahcache/cache.class.php';
+require_once __DIR__ . '/../../vendor/axelhahn/ahcache/cache.class.php';
 
 /**
  * rollout_base class that will be extended in a rollout plugin
  * see deployment/plugins/rollout/*
  * 
  * @author axel
+ * 
+ * Axel <axel.hahn@unibe.ch>
+ * 2024-08-29  Axel php8 only; added variable types; short array syntax
  */
-class rollout_base implements iRolloutplugin{
-    
+class rollout_base implements iRolloutplugin
+{
+
     // ---------------------------------------------------------------
     // VARIABLES
     // ---------------------------------------------------------------
@@ -18,58 +22,64 @@ class rollout_base implements iRolloutplugin{
      * settings in the config structore for global and project based config
      * @var string
      */
-    protected $_sPluginId='UNSET';
+    protected string $_sPluginId = 'UNSET';
+
     /**
-     * data with plugin infos (read from info.json)
+     * Data with plugin infos (read from info.json)
      * @var array
      */
-    protected $_aPlugininfos=false;
-    
+    protected array $_aPlugininfos = [];
+
     /**
-     * array with translation texts
-     * @var type
+     * Array with translation texts
+     * @var array
      */
-    protected $_aLang=false;
+    protected array $_aLang = [];
 
     /**
      * set language; 2 letter code, i.e. "de"; default language is "en" ; a 
      * file "lang_en.json" is required in the plugin dir
      * @var string
      */
-    protected $_sFallbackLang = 'en-en';
+    protected string $_sFallbackLang = 'en-en';
 
     /**
      * set language; 2 letter code, i.e. "de"; default language is "en" ; a 
      * file "lang_en.json" is required in the plugin dir
      * @var string
      */
-    protected $_sLang = 'en-en';
-    
+    protected string $_sLang = 'en-en';
+
     /**
      * string with phase of project; one of preview|stage|live
-     * @var type
+     * @var string
      */
-    protected $_sPhase = false;
-    
+    protected string $_sPhase = '';
+
     /**
      * global configuration of the rollout plugin
      * @var array
      */
-    protected $_aCfgGlobal = false;
+    protected array $_aCfgGlobal = [];
+
     /**
      * configuration of the project
      * @var array
      */
-    protected $_aCfgProject = false;
-    
-    protected $_sNamePrefix4Project=false; // set in constructor
-    protected $_sNamePrefix4Phase=false; // set in constructor
-    
+    protected array $_aCfgProject = [];
+
+    /*
+    UNUSED
+    protected string $_sNamePrefix4Project = ''; // set in constructor
+    protected string $_sNamePrefix4Phase = ''; // set in constructor
+    */
+
     // ---------------------------------------------------------------
     // CONSTRUCTOR
     // ---------------------------------------------------------------
 
     /**
+     * Constructor
      * initialize rollout plugin
      * @param array $aParams  hash with those possible keys
      *                  lang         string   language, i.e. 'de'
@@ -79,259 +89,269 @@ class rollout_base implements iRolloutplugin{
      *                                        for project and all phases
      * @return boolean
      */
-    public function __construct($aParams) {
-        
+    public function __construct($aParams)
+    {
+
         // set current plugin id - taken from plugin directory name above
-        $oReflection=new ReflectionClass($this);
-        $this->_sPluginId=basename(dirname($oReflection->getFileName()));
-   
+        $oReflection = new ReflectionClass($this);
+        $this->_sPluginId = basename(dirname($oReflection->getFileName()));
+
         // ----- init language
-        if (isset($aParams['lang'])){
+        if (isset($aParams['lang'])) {
             $this->setLang($aParams['lang']);
         } else {
             $this->setLang();
         }
-        
+
         // ----- init phase
-        if (isset($aParams['phase'])){
+        if (isset($aParams['phase'])) {
             $this->setPhase($aParams['phase']);
         }
 
         // ----- init global config
-        if (isset($aParams['globalcfg'])){
+        if (isset($aParams['globalcfg'])) {
             $this->setGlobalConfig($aParams['globalcfg']);
         }
         // ----- init project config
-        if (isset($aParams['projectcfg'])){
+        if (isset($aParams['projectcfg'])) {
             $this->setProjectConfig($aParams['projectcfg']);
         }
-        return true;
     }
-    
+
     // ---------------------------------------------------------------
     // FORM HELPER
     // ---------------------------------------------------------------
 
     /**
-     * get a string for a prefix for name attribute in form vars. 
+     * Get a string for a prefix for name attribute in form vars. 
      * It is important to store the value in the wanted structure.
      * 
-     * @param type $sPhase
-     * @return type
+     * @param string $sPhase
+     * @return string
      */
-    protected function _getNamePrefix($sPhase=false){
+    protected function _getNamePrefix(string $sPhase = ''): string
+    {
         return ($sPhase
-            ? 'phases['.$sPhase.'][plugins][rollout]['.$this->getId().']'
-            : 'plugins[rollout]['.$this->getId().']'
+            ? 'phases[' . $sPhase . '][plugins][rollout][' . $this->getId() . ']'
+            : 'plugins[rollout][' . $this->getId() . ']'
         );
     }
-    
+
     /**
-     * get Data from a callback function and store it in a cache
+     * Get Data from a callback function and store it in a cache
      * The response type depends on the callback function
      * 
      * @param string   $sFunctionname  name of the callback function
      * @param string   $sKey           name of the key; "project" or name of phase
-     * @param integr   $iTtl           ttl value = how many seconds to use cache
-     * @param integr   $iTtlOnError    ttl value = how many seconds to use cache if there was no response
-     * @return type
+     * @param integer  $iTtl           ttl value = how many seconds to use cache
+     * @param integer  $iTtlOnError    ttl value = how many seconds to use cache if there was no response
+     * @return mixed
      */
-    protected function _getCallback($sFunctionname, $sKey, $iTtl=15,$iTtlOnError=10){
-        $oCache=new AhCache('rollout-'.$this->getId(), 'callback-'.$sFunctionname.'-'.$sKey);
-        if($oCache->isExpired()){
-            $aMydata= call_user_func(array($this, $sFunctionname));
+    protected function _getCallback(string $sFunctionname, string $sKey, int $iTtl = 15, int $iTtlOnError = 10): mixed
+    {
+        $oCache = new AhCache('rollout-' . $this->getId(), 'callback-' . $sFunctionname . '-' . $sKey);
+        if ($oCache->isExpired()) {
+            $aMydata = call_user_func([$this, $sFunctionname]);
             // echo "$sFunctionname fresh ".($aMydata ? "OK": "false")." - storing for $iTtl sec<br>";
             $oCache->write($aMydata, ($aMydata ? $iTtl : $iTtlOnError));
         } else {
             // echo "$sFunctionname from cache ... ".$oCache->iExpired()." sec <br>";
-            $aMydata=$oCache->read();
+            $aMydata = $oCache->read();
         }
         // echo '<pre>'.print_r($aMydata, 1).'</pre>'; die(__METHOD__);
         return $aMydata;
     }
+
     /**
-     * render a form by given form elementes 
+     * Get Html code for a form by given form elementes 
+     * 
      * @param  array   $aFormdata  array of form elements
      * @param  string  $sKey       part of the identifier used in id of the input field
      * @return string
      */
-    protected function _renderForm($aFormdata, $sKey){
+    protected function _renderForm(array $aFormdata, string $sKey): string
+    {
         static $i;
-        if (!isset($i)){
-            $i=0;
+        if (!isset($i)) {
+            $i = 0;
         }
 
-        $sReturn='';
-        $sKeyPrefix=$this->getId().'_'.$sKey;
-        
+        $sReturn = '';
+        $sKeyPrefix = $this->getId() . '_' . $sKey;
+
         $oForm = new formgen();
         foreach ($aFormdata as $elementData) {
-            $elementKey=$sKeyPrefix.'_'.$i++;
-            $sReturn.=$oForm->renderHtmlElement($elementKey, $elementData);
+            $elementKey = $sKeyPrefix . '_' . $i++;
+            $sReturn .= $oForm->renderHtmlElement($elementKey, $elementData);
         }
         return $sReturn;
     }
-    
+
     /**
-     * render form fields for global plugin variables
+     * Get Html code for form fields for global plugin variables
+     * 
      * @param string  $sScope       scope of vars ... one of global|project|phase
      * @param string  $sPhase       optional: render global vars in a phase; if no phase was set it renders form fields for project based settings
      * @return string
      */
-    protected function _renderForm4Vars($sScope, $sPhase=false){
-        $sReturn='';
-        
+    protected function _renderForm4Vars(string $sScope, string $sPhase = ''): string
+    {
+        $sReturn = '';
+
         // test vars from info.json file
-        $aInfos=$this->getPluginInfos();
-        if(!isset($aInfos['vars'][$sScope]) || !count($aInfos['vars'][$sScope])){
+        $aInfos = $this->getPluginInfos();
+        if (!isset($aInfos['vars'][$sScope]) || !count($aInfos['vars'][$sScope])) {
             return '';
         }
-        
-        $sKey=($sPhase ? 'phase_'.$sPhase : 'project');
-        $sPrefixName=$this->_getNamePrefix($sPhase);
-        
+
+        $sKey = ($sPhase ? 'phase_' . $sPhase : 'project');
+        $sPrefixName = $this->_getNamePrefix($sPhase);
+
         // set defaults - to be used in placeholder attribute
         // no phase = project level - take global defaults of ci config
         // on a phase - fetch merged cofig data of project
-        $aDefaultValues=($sPhase ? $this->getConfig() : $this->_aCfgGlobal);
-        $aDefaultSource=($sPhase ? 'project' : 'global');
-                
+        $aDefaultValues = ($sPhase ? $this->getConfig() : $this->_aCfgGlobal);
+        $aDefaultSource = ($sPhase ? 'project' : 'global');
+
         // set defaults - to be used in value attribute
-        $aValues=($sPhase 
-                ? $this->_aCfgProject['phases'][$sPhase]['plugins']['rollout'][$this->getId()] 
-                : $this->_aCfgProject['plugins']['rollout'][$this->getId()]
+        $aValues = ($sPhase
+            ? $this->_aCfgProject['phases'][$sPhase]['plugins']['rollout'][$this->getId()]
+            : $this->_aCfgProject['plugins']['rollout'][$this->getId()]
         );
-        
- 
+
+
         // create form fields
-        // $aFormdata[]=array('type' => 'markup','value' => '<br>'.$this->_t('section-override-'.$sScope.'-vars').':');
-        $aFormdata[]=array('type' => 'markup','value' => '<div style="style: clear: left;"></div><h4>'.$this->getId() .' :: '. $sScope.'</h4>');
-        
-        $sMiss='';
-        foreach ($aInfos['vars'][$sScope] as $sVarname=>$aVarinfos){
-            if ($sScope==='global' && !isset($this->_aCfgGlobal[$sVarname])){
-                $sMiss.='- plugin var was not set in global CI config: "'.$sVarname.'".<br>';
+        // $aFormdata[]=['type' => 'markup','value' => '<br>'.$this->_t('section-override-'.$sScope.'-vars').':'];
+        $aFormdata[] = ['type' => 'markup', 'value' => '<div style="style: clear: left;"></div><h4>' . $this->getId() . ' :: ' . $sScope . '</h4>'];
+
+        $sMiss = '';
+        foreach ($aInfos['vars'][$sScope] as $sVarname => $aVarinfos) {
+            if ($sScope === 'global' && !isset($this->_aCfgGlobal[$sVarname])) {
+                $sMiss .= '- plugin var was not set in global CI config: "' . $sVarname . '".<br>';
             }
-            
-            
-            $sMyPlaceholder=(isset($aDefaultValues[$sVarname]) 
-                                ? htmlentities($aDefaultValues[$sVarname]) 
-                                : (isset($aVarinfos['default']) ? $aVarinfos['default'] : 'N.A.')
-                );
-            
+
+
+            $sMyPlaceholder = (isset($aDefaultValues[$sVarname])
+                ? htmlentities($aDefaultValues[$sVarname])
+                : (isset($aVarinfos['default']) ? $aVarinfos['default'] : 'N.A.')
+            );
+
             // if a callback was set for this variable
-            if(isset($aVarinfos['callback'])){
-                $aCallbackData=$this->_getCallback(
-                        $aVarinfos['callback'], 
-                        (isset($aVarinfos['per_scope']) && $aVarinfos['per_scope'] ? $sKey : ''), 
-                        (isset($aVarinfos['ttl']) ? $aVarinfos['ttl'] : 60)
+            if (isset($aVarinfos['callback'])) {
+                $aCallbackData = $this->_getCallback(
+                    $aVarinfos['callback'],
+                    (isset($aVarinfos['per_scope']) && $aVarinfos['per_scope'] ? $sKey : ''),
+                    (isset($aVarinfos['ttl']) ? $aVarinfos['ttl'] : 60)
                 );
-                if(!$aCallbackData){
-                    $aVarinfos['type']='text';
+                if (!$aCallbackData) {
+                    $aVarinfos['type'] = 'text';
                 } else {
-                    $aEffectiveConfig=$this->getConfig($sPhase);
+                    $aEffectiveConfig = $this->getConfig($sPhase);
                     // echo $sKey.' ... '; print_r($aEffectiveConfig[$sVarname]); echo '<br>';
-                    
+
                     // mark entry as active ... for select and radio
-                    if(isset($aEffectiveConfig[$sVarname]) && isset($aCallbackData[$aEffectiveConfig[$sVarname]])){
-                        $aCallbackData[$aEffectiveConfig[$sVarname]]['selected']='selected';
-                        $aCallbackData[$aEffectiveConfig[$sVarname]]['checked']='checked';
-                        $aCallbackData[$aEffectiveConfig[$sVarname]]['label'].=' &laquo;&laquo;';
-                    } elseif ($aVarinfos['type']==='select') {
-                        $aCallbackData=array_merge(array('NO_SELECTED_ITEM_YET'=>array('value'=>'', 'label'=>'...')), $aCallbackData);
+                    if (isset($aEffectiveConfig[$sVarname]) && isset($aCallbackData[$aEffectiveConfig[$sVarname]])) {
+                        $aCallbackData[$aEffectiveConfig[$sVarname]]['selected'] = 'selected';
+                        $aCallbackData[$aEffectiveConfig[$sVarname]]['checked'] = 'checked';
+                        $aCallbackData[$aEffectiveConfig[$sVarname]]['label'] .= ' &laquo;&laquo;';
+                    } elseif ($aVarinfos['type'] === 'select') {
+                        $aCallbackData = array_merge(['NO_SELECTED_ITEM_YET' => ['value' => '', 'label' => '...']], $aCallbackData);
                     }
- 
+
                     // wenn value = defaultvalue, dann value auf '' setzen (damit bei Default vom Scope
                     // davor ein Leerstring übergeben wird - analog onfocusout im Text Input
-                    if (isset($aCallbackData[$aDefaultValues[$sVarname]])){
-                        $aCallbackData[$aDefaultValues[$sVarname]]['value']='';
-                        $aCallbackData[$aDefaultValues[$sVarname]]['label'].=' (*)';
-                    } 
+                    if (isset($aCallbackData[$aDefaultValues[$sVarname]])) {
+                        $aCallbackData[$aDefaultValues[$sVarname]]['value'] = '';
+                        $aCallbackData[$aDefaultValues[$sVarname]]['label'] .= ' (*)';
+                    }
                     // print_r($aCallbackData[$sVarname]); echo "<br>";
                 }
                 // echo '<pre>'.$sCallbackfunktion .' = '. print_r($aMydata,1 ).'</pre>';
             }
             switch ($aVarinfos['type']) {
                 case "password":
-                    $sMyPlaceholder=(isset($aDefaultValues[$sVarname]) 
-                                        ? '******************************'
-                                        : $sMyPlaceholder
-                        );
-                    $aFormdata[]=array(
+                    $sMyPlaceholder = (isset($aDefaultValues[$sVarname])
+                        ? '******************************'
+                        : $sMyPlaceholder
+                    );
+                    $aFormdata[] = [
                         'type' => $aVarinfos['type'],
-                        'name' => $sPrefixName.'['.$sVarname.']',
-                        'label' => $this->_t($sVarname.'-label'),
-                        'title' => $this->_t($sVarname.'-hint'),
+                        'name' => $sPrefixName . '[' . $sVarname . ']',
+                        'label' => $this->_t($sVarname . '-label'),
+                        'title' => $this->_t($sVarname . '-hint'),
                         'value' => (isset($aValues[$sVarname]) ? htmlentities($aValues[$sVarname]) : ''),
                         // 'required' => 'required',
                         'validate' => 'isastring',
                         // 'size' => 25,
                         'placeholder' => $sMyPlaceholder,
                         'autocomplete' => 'off',
-                    );
+                    ];
                     break;
                 case "select":
                 case "radio":
-                    $aOptions=$aCallbackData;
-                    $aFormdata[]=array(
+                    $aOptions = $aCallbackData;
+                    $aFormdata[] = [
                         'type' => $aVarinfos['type'],
-                        'name' => $sPrefixName.'['.$sVarname.']',
-                        'label' => $this->_t($sVarname.'-label'),
-                        'title' => $this->_t($sVarname.'-hint'),
+                        'name' => $sPrefixName . '[' . $sVarname . ']',
+                        'label' => $this->_t($sVarname . '-label'),
+                        'title' => $this->_t($sVarname . '-hint'),
 
                         'validate' => 'isastring',
                         'options' => $aOptions,
-                        
+
                         // 'placeholder' => $sMyPlaceholder       
-                    );
+                    ];
                     break;
                 case "text":
-                    $aFormdata[]=array(
+                    $aFormdata[] = [
                         'type' => $aVarinfos['type'],
-                        'name' => $sPrefixName.'['.$sVarname.']',
-                        'label' => $this->_t($sVarname.'-label'),
-                        'ondblclick' => ($aDefaultValues[$sVarname] ? 'if (this.value==\'\') { this.value=\''.$aDefaultValues[$sVarname].'\' }' : ''),
-                        'onfocusout' => ($aDefaultValues[$sVarname] ? 'if (this.value==\''.$aDefaultValues[$sVarname].'\') { this.value=\'\' }' : ''),
-                        'title' => htmlentities($this->_t($sVarname.'-hint')."\n"
-                            . ($this->_aCfgGlobal[$sVarname] ? '- global: '.$this->_aCfgGlobal[$sVarname]."\n" : '')
-                            . ($this->_aCfgProject['plugins']['rollout'][$this->getId()][$sVarname] ? '- project: '.$this->_aCfgProject['plugins']['rollout'][$this->getId()][$sVarname]."\n" : '')
-                            )
-                            ,
+                        'name' => $sPrefixName . '[' . $sVarname . ']',
+                        'label' => $this->_t($sVarname . '-label'),
+                        'ondblclick' => ($aDefaultValues[$sVarname] ? 'if (this.value==\'\') { this.value=\'' . $aDefaultValues[$sVarname] . '\' }' : ''),
+                        'onfocusout' => ($aDefaultValues[$sVarname] ? 'if (this.value==\'' . $aDefaultValues[$sVarname] . '\') { this.value=\'\' }' : ''),
+                        'title' => htmlentities(
+                            $this->_t($sVarname . '-hint') . "\n"
+                            . ($this->_aCfgGlobal[$sVarname] ? '- global: ' . $this->_aCfgGlobal[$sVarname] . "\n" : '')
+                            . ($this->_aCfgProject['plugins']['rollout'][$this->getId()][$sVarname] ? '- project: ' . $this->_aCfgProject['plugins']['rollout'][$this->getId()][$sVarname] . "\n" : '')
+                        )
+                        ,
                         'value' => (isset($aValues[$sVarname]) ? htmlentities($aValues[$sVarname]) : ''),
                         // 'required' => 'required',
                         'validate' => 'isastring',
                         // 'size' => 25,
                         'placeholder' => $sMyPlaceholder,
                         'autocomplete' => 'off',
-                    );
+                        ];
                     break;
 
                 default:
-                   $sMiss.='- plugin var "'.$sVarname.'" was not rendered - its type "'.$aVarinfos['type'].'" is not supported in the general form renderer.<br>';
-                   break;
+                    $sMiss .= '- plugin var "' . $sVarname . '" was not rendered - its type "' . $aVarinfos['type'] . '" is not supported in the general form renderer.<br>';
+                    break;
             }
         }
-        // $aFormdata[]=array('type' => 'markup','value' => '<div style="style: clear: left;"></div><br><br>');
+        // $aFormdata[]=['type' => 'markup','value' => '<div style="style: clear: left;"></div><br><br>'];
         return $this->_renderForm($aFormdata, $sKey)
-            . ($sMiss 
-                ? '<pre>WARNINGS:<br>'.$sMiss.'</pre>' . ($sScope==='global' ? $this -> renderCfgExample() : '' )
+            . ($sMiss
+                ? '<pre>WARNINGS:<br>' . $sMiss . '</pre>' . ($sScope === 'global' ? $this->renderCfgExample() : '')
                 : ''
             )
-            ;
+        ;
     }
-    
+
     /**
-     * get a translated text from lang_XX.json in plugin dir;
+     * Get a translated text from lang_XX.json in plugin dir;
      * If the key is missed it returns "[KEY :: LANG]"
      * 
      * @see setLang()
+     * 
      * @param string $sKey  key to find in lang file
      * @return string
      */
-    protected function _t($sKey){
+    protected function _t(string $sKey): string
+    {
         return (isset($this->_aLang[$sKey]) && $this->_aLang[$sKey])
-                ? $this->_aLang[$sKey]
-                : "[ $sKey :: $this->_sLang ]"
+            ? $this->_aLang[$sKey]
+            : "[ $sKey :: $this->_sLang ]"
         ;
     }
 
@@ -345,125 +365,145 @@ class rollout_base implements iRolloutplugin{
      * translated texts can be done with $this->_t("your_key")
      * 
      * @see _t()
+     * 
      * @param string   $sLang  language code, i.e. "de-de"
      * @return boolean
      */
-    public function setLang($sLang=false){
-        $this->_sLang=$sLang ? $sLang : $this->_sLang;
-        
-        $oReflection=new ReflectionClass($this);
-        $sFile=dirname($oReflection->getFileName()) . '/lang_'.$this->_sLang.'.json';
-        if (!file_exists($sFile)){
-            $sFile=dirname($oReflection->getFileName()) . '/lang_'.$this->_sFallbackLang.'.json';
-            $this->_sLang=$this->_sFallbackLang;
+    public function setLang(string $sLang = ''): bool
+    {
+        $this->_sLang = $sLang ? $sLang : $this->_sLang;
+
+        $oReflection = new ReflectionClass($this);
+        $sFile = dirname($oReflection->getFileName()) . '/lang_' . $this->_sLang . '.json';
+        if (!file_exists($sFile)) {
+            $sFile = dirname($oReflection->getFileName()) . '/lang_' . $this->_sFallbackLang . '.json';
+            $this->_sLang = $this->_sFallbackLang;
         }
-        $this->_aLang=(file_exists($sFile)) ? json_decode(file_get_contents($sFile), 1) : $this->_aLang;
+        $this->_aLang = (file_exists($sFile)) ? json_decode(file_get_contents($sFile), 1) : $this->_aLang;
         return true;
     }
-    
+
     /**
      * set a phase for automatic use GETTER methods
+     * 
+     * @param string $sPhase  name of the phase; one of preview|stage|live
+     * @return boolean
      */
-    public function setPhase($sPhase){
-        $this->_sPhase=$sPhase;
+    public function setPhase(string $sPhase): bool
+    {
+        $this->_sPhase = $sPhase;
         return true;
     }
-    
+
 
     // ----------------------------------------------------------------------
     // INTERFACE :: CHECKS
     // ----------------------------------------------------------------------
 
     /**
-     * check requirements if the plugin could work
+     * Get an array with shell commands to check requirements if the plugin
+     * can work
+     * 
+     * @return array
      */
-    public function checkRequirements(){
-        // no specific checks needed ... always true
-        return true;
+    public function checkRequirements(): array
+    {
+        // no specific checks needed ... always empty
+        return [];
     }
 
     /**
-     * check access to a deploy target
+     * Get an array with shell commands to check access to a deploy target
+     * 
+     * @return array
      */
-    public function checkConnectionToTarget(){
+    public function checkConnectionToTarget(): array
+    {
         // do nothing ... always true
-        return true;
+        return [];
     }
 
     // ----------------------------------------------------------------------
     // INTERFACE :: SETTER
     // ----------------------------------------------------------------------
 
-
     /**
-     * set Config ... by given global config of the current plugin
-     * @param array $aConfigArray 
+     * Set Config ... by given global config of the current plugin
+     * 
+     * @param array $aConfigArray complete array of all config data
+     * @return boolean
      */
-    public function setGlobalConfig($aConfigArray){
-        return $this->_aCfgGlobal=$aConfigArray;
+    public function setGlobalConfig(array $aConfigArray): bool
+    {
+        $this->_aCfgGlobal = $aConfigArray;
+        return true;
     }
 
-
-
     /**
-     * set Config ... by given project config
+     * Set Config ... by given project config
+     * 
+     * @param array $aProjectConfigArray complete array of project config
+     * @return array
      */
-    public function setProjectConfig($aProjectConfigArray){
-        $this->_aCfgProject=$aProjectConfigArray;
+    public function setProjectConfig(array $aProjectConfigArray): array
+    {
+        $this->_aCfgProject = $aProjectConfigArray;
         // echo '<pre>'.print_r($aProjectConfigArray, 1).'</pre>';
         // ----- ensure that the config structure exists 
         // (it is easier fo handling in getConfig())
-        if (!isset($this->_aCfgProject['plugins']['rollout'][$this->_sPluginId])){
+        if (!isset($this->_aCfgProject['plugins']['rollout'][$this->_sPluginId])) {
             /*
             if (!isset($this->_aCfgProject['plugins']['rollout'])){
                 if (!isset($this->_aCfgProject['plugins'])){
-                    $this->_aCfgProject['plugins']=array();
+                    $this->_aCfgProject['plugins']=[];
                 }
-                $this->_aCfgProject['plugins']['rollout']=array();
+                $this->_aCfgProject['plugins']['rollout']=[];
             }
              * 
              */
-            $this->_aCfgProject['plugins']['rollout'][$this->_sPluginId]=array('INFO'=>'created');
+            $this->_aCfgProject['plugins']['rollout'][$this->_sPluginId] = ['INFO' => 'created'];
         }
-        
+
         // unset empty project values to get global values
 
-        foreach ($this->_aCfgProject['plugins']['rollout'][$this->_sPluginId] as $sVarname=>$value){
-            if ($value===''){
+        foreach ($this->_aCfgProject['plugins']['rollout'][$this->_sPluginId] as $sVarname => $value) {
+            if ($value === '') {
                 unset($this->_aCfgProject['plugins']['rollout'][$this->_sPluginId][$sVarname]);
             }
         }
-        foreach (array_keys($this->_aCfgProject['phases']) as $sMyPhase){
-            if (isset($this->_aCfgProject['phases'][$sMyPhase]['plugins']['rollout'][$this->getId()])){
-                foreach($this->_aCfgProject['phases'][$sMyPhase]['plugins']['rollout'][$this->getId()] as $sVarname=>$value){
-                    if ($value===''){
+        foreach (array_keys($this->_aCfgProject['phases']) as $sMyPhase) {
+            if (isset($this->_aCfgProject['phases'][$sMyPhase]['plugins']['rollout'][$this->getId()])) {
+                foreach ($this->_aCfgProject['phases'][$sMyPhase]['plugins']['rollout'][$this->getId()] as $sVarname => $value) {
+                    if ($value === '') {
                         unset($this->_aCfgProject['phases'][$sMyPhase]['plugins']['rollout'][$this->getId()][$sVarname]);
                     }
                 }
             }
         }
-        // TODO: 
+        
         return $this->_aCfgProject;
     }
-    
+
     // ----------------------------------------------------------------------
     // INTERFACE :: GETTER
     // ----------------------------------------------------------------------
 
     /**
-     * get configuration for the project .. or more specifi for a given phase
+     * Get configuration array for the project .. or more specific for a given phase
+     * 
      * @param  string   $sPhase
      * @param  boolean  $bMask   Flag for public output; if true then mask your secrets
      * @return array
      */
-    public function getConfig($sPhase=false, $bMask=false){
+    public function getConfig(string $sPhase = '', bool $bMask = false): array
+    {
 
-        $aReturn=array_merge($this->_aCfgGlobal, $this->_aCfgProject['plugins']['rollout'][$this->getId()]);
-        if($sPhase && isset($this->_aCfgProject['phases'][$sPhase]['plugins']['rollout'][$this->getId()])){
-            $aReturn=array_merge($aReturn, $this->_aCfgProject['phases'][$sPhase]['plugins']['rollout'][$this->getId()]);
+        $aReturn = array_merge($this->_aCfgGlobal, $this->_aCfgProject['plugins']['rollout'][$this->getId()]);
+        if ($sPhase && isset($this->_aCfgProject['phases'][$sPhase]['plugins']['rollout'][$this->getId()])) {
+            $aReturn = array_merge($aReturn, $this->_aCfgProject['phases'][$sPhase]['plugins']['rollout'][$this->getId()]);
         }
-        if ($bMask && isset($aReturn['password'])){
-            $aReturn['password']='**********';
+        if ($bMask && isset($aReturn['password'])) {
+            $aReturn['password'] = '**********';
         }
         return $aReturn;
         /*
@@ -473,58 +513,67 @@ class rollout_base implements iRolloutplugin{
         ;
         */
     }
-    
+
     /**
-     * get an array with shell commands to execute
+     * Get an array with shell commands to execute for deployment of built file
+     * 
      * @param  string   $sPhase
      * @param  boolean  $bMask   Flag for public output; if true then mask your secrets
      * @return array
      */
-    public function getDeployCommands($sPhase, $bMask=false){
+    public function getDeployCommands(string $sPhase, bool $bMask = false): array
+    {
         return [
-            'echo "ERROR: The method getDeployCommamds($sPhase) was not implemented in the rollout plugin ['.$this->getId().']"',
+            'echo "ERROR: The method getDeployCommamds($sPhase) was not implemented in the rollout plugin [' . $this->getId() . ']"',
             'exit 1'
-            ];
+        ];
     }
-    
+
     /**
-     * get string with current ID
+     * Get string with current plugin ID
+     * 
      * @return string
      */
-    public function getId(){
+    public function getId(): string
+    {
         return $this->_sPluginId;
     }
-    
+
     /**
-     * get string with plugin name (taken from plugin language file)
+     * Get name of plugin as string ... language specific
+     * 
      * @return string
      */
-    public function getName(){
+    public function getName(): string
+    {
         return $this->_t('plugin_name');
     }
-    
+
     /**
-     * get string with plugin description (taken from plugin language file)
+     * Get description of plugin as string ... language specific
      * @return string
      */
-    public function getDescription(){
+    public function getDescription(): string
+    {
         return $this->_t('description');
     }
+
     /**
-     * get array read from info.json
-     * @return type
+     * Get array of data in info.js
+     * @return array
      */
-    public function getPluginInfos(){
+    public function getPluginInfos(): array
+    {
 
-        if ($this->_aPlugininfos){
+        if ($this->_aPlugininfos) {
             return $this->_aPlugininfos;
         }
-        
-        $oReflection=new ReflectionClass($this);
-        $sFile=dirname($oReflection->getFileName()) . '/info.json';
-        $this->_aPlugininfos= (file_exists($sFile))
+
+        $oReflection = new ReflectionClass($this);
+        $sFile = dirname($oReflection->getFileName()) . '/info.json';
+        $this->_aPlugininfos = (file_exists($sFile))
             ? json_decode(file_get_contents($sFile), 1)
-            : array('error'=> 'unable to read info file ['.$sFile.'].')
+            : ['error' => 'unable to read info file [' . $sFile . '].']
         ;
         return $this->_aPlugininfos;
     }
@@ -532,71 +581,101 @@ class rollout_base implements iRolloutplugin{
     // ----------------------------------------------------------------------
     // INTERFACE :: RENDERER
     // ----------------------------------------------------------------------
-    public function renderCfgExample(){
-        $sReturn='';
-        $sPre='                ';
-        
-        $aInfos=$this->getPluginInfos();
-        $sReturn.='<pre>$aConfig = array(
+
+    /**
+     * Get HTML code for example configuration
+     * @return string
+     */
+    public function renderCfgExample()
+    {
+        $sReturn = '';
+        $sPre = '                ';
+
+        $aInfos = $this->getPluginInfos();
+        $sReturn .= '<pre>$aConfig = [
     ...
-    \'plugins\'=>array(
+    \'plugins\'=>[
         ...
         // enabled rollout plugins
-        \'rollout\'=>array(
+        \'rollout\'=>[
             ...
             <strong>
-            \''.$this->getId().'\'=>array(
-                // '.$this->getName().'
-                // '.$this->getDescription().'
-                '.PHP_EOL;
-        
+            \'' . $this->getId() . '\'=>[
+                // ' . $this->getName() . '
+                // ' . $this->getDescription() . '
+                ' . PHP_EOL;
+
         // add global vars
-        if(!isset($aInfos['vars']['global']) || !count($aInfos['vars']['global'])){
-            $sReturn.=$sPre.'// this plugin has no global config vars'.PHP_EOL;
+        if (!isset($aInfos['vars']['global']) || !count($aInfos['vars']['global'])) {
+            $sReturn .= $sPre . '// this plugin has no global config vars' . PHP_EOL;
         } else {
-            foreach ($aInfos['vars']['global'] as $sVar=>$aItem){
-                $sReturn.=$sPre.'// '.$this->_t($sVar.'-hint').PHP_EOL;
-                $sReturn.=$sPre.'\''.$sVar.'\'=>\''.(isset($this->_aCfgGlobal[$sVar]) ? $this->_aCfgGlobal[$sVar] : $aItem['default']).'\','.PHP_EOL;
-                $sReturn.=PHP_EOL;
+            foreach ($aInfos['vars']['global'] as $sVar => $aItem) {
+                $sReturn .= $sPre . '// ' . $this->_t($sVar . '-hint') . PHP_EOL;
+                $sReturn .= $sPre . '\'' . $sVar . '\'=>\'' . (isset($this->_aCfgGlobal[$sVar]) ? $this->_aCfgGlobal[$sVar] : $aItem['default']) . '\',' . PHP_EOL;
+                $sReturn .= PHP_EOL;
             }
         }
-        
-        $sReturn.='
-            ),
+
+        $sReturn .= '
+            ],
             </strong>
             ...
-        ),
+        ],
         ...
-    ),
-);</pre>';
+    ],
+];</pre>';
         return $sReturn;
     }
-    protected function _renderMoreToggler($sContent){
-        $sDivId='rollout-more-toggler-'.$this->getId().'-'.md5($sContent);
+
+    /**
+     * Get html code for button [...] that shows and hides more information
+     * 
+     * @param string $sContent
+     * @return string
+     */
+    protected function _renderMoreToggler(string $sContent)
+    {
+        $sDivId = 'rollout-more-toggler-' . $this->getId() . '-' . md5($sContent);
         return ''
-                . '<button onclick="$(\'#'.$sDivId.'\').slideToggle(); return false;" class="btn btn-secondary"> ... </button>'
-                . '<div id="'.$sDivId.'" style="display: none;">'
-                    . $sContent
-                . '</div>'
-                ;
-        
+            . '<button onclick="$(\'#' . $sDivId . '\').slideToggle(); return false;" class="btn btn-secondary"> ... </button>'
+            . '<div id="' . $sDivId . '" style="display: none;">'
+            . $sContent
+            . '</div>'
+        ;
+
     }
-    public function renderFormdata4Project() {
+
+    /**
+     * Override general form renderer: show 
+     * - formvars for project
+     * 
+     * @return string
+     */
+    public function renderFormdata4Project(): string
+    {
         return ''
-                . $this->_renderForm4Vars('project', false)
-                . $this->_renderForm4Vars('global', false)
-                // . $this->_renderFormProjectVars($this->_sNamePrefix4Project, false)
-                // . '<pre>DEBUG: GLOBAL settings - $this->_aCfgGlobal = ' . print_r($this->_aCfgGlobal, 1) . '</pre>'
-                // . '<pre>DEBUG: PROJECT settings - $this->getConfig() = ' . print_r($this->getConfig(), 1) . '</pre>'
-        // .'<pre>DEBUG: $this->_aCfgProject ... plugin = '.print_r($this->_aCfgProject, 1).'</pre>'
+            . $this->_renderForm4Vars('project', false)
+            . $this->_renderForm4Vars('global', false)
+            // . $this->_renderFormProjectVars($this->_sNamePrefix4Project, false)
+            // . '<pre>DEBUG: GLOBAL settings - $this->_aCfgGlobal = ' . print_r($this->_aCfgGlobal, 1) . '</pre>'
+            // . '<pre>DEBUG: PROJECT settings - $this->getConfig() = ' . print_r($this->getConfig(), 1) . '</pre>'
+            // .'<pre>DEBUG: $this->_aCfgProject ... plugin = '.print_r($this->_aCfgProject, 1).'</pre>'
         ;
     }
-    public function renderFormdata4Phase($sPhase){
+
+    /**
+     * override of form renderer: show configuration for a given phase
+     
+     * @param string $sPhase  phaese; one of preview|stage|live
+     * @return string
+     */
+    public function renderFormdata4Phase(string $sPhase): string
+    {
         static $iCounter;
-        if(!isset($iCounter)){
-            $iCounter=0;
+        if (!isset($iCounter)) {
+            $iCounter = 0;
         }
-        $sDivId='rollout-override-div-'.$this->getId().'-'.$sPhase.'-'.$iCounter;
+        $sDivId = 'rollout-override-div-' . $this->getId() . '-' . $sPhase . '-' . $iCounter;
         return ''
             . $this->_renderForm4Vars('phase', $sPhase)
             . $this->_renderMoreToggler(
@@ -608,6 +687,6 @@ class rollout_base implements iRolloutplugin{
             // . '<pre>DEBUG: GLOBAL settings - $this->_aCfgGlobal = ' . print_r($this->_aCfgGlobal, 1) . '</pre>'
             // . '<pre>DEBUG: PROJECT settings - $this->getConfig() = ' . print_r($this->getConfig(), 1) . '</pre>'
             // . '<pre>DEBUG: PHASE settings - $this->getConfig("'.$sMyPhase.'") = ' . print_r($this->getConfig($sMyPhase), 1) . '</pre>'
-            ;
+        ;
     }
 }
diff --git a/public_html/deployment/classes/sws.class.php b/public_html/deployment/classes/sws.class.php
index 1405b255e3a7d6a4975a9df17c2425fecc7fcac9..19194e0a479ae2652ed13dd37a915655909d1006 100644
--- a/public_html/deployment/classes/sws.class.php
+++ b/public_html/deployment/classes/sws.class.php
@@ -141,7 +141,6 @@ class sws {
         if (is_array($aKnownClasses)) {
             $this->setConfig($aKnownClasses);
         }
-        return true;
     }
 
     // ----------------------------------------------------------------------
diff --git a/public_html/deployment/classes/user.class.php b/public_html/deployment/classes/user.class.php
index 187bbdd52dfb1ec106fabf8f34d0270ae6f08753..adb85a9ee86b7a314efad7977ba9df086e68af02 100644
--- a/public_html/deployment/classes/user.class.php
+++ b/public_html/deployment/classes/user.class.php
@@ -5,106 +5,126 @@
  * This class is used in the base class
  *
  * @author hahn
+ * 
+ * Axel <axel.hahn@unibe.ch>
+ * 2024-08-29  Axel php8 only; added variable types; use short array syntax
  */
-class user {
-    
+class user
+{
+
     /**
      * login name of the current user
      * @var string
      */
-    private $_sUsername=false;
-    
+    private string $_sUsername = '';
+
     /**
      * list of groups of the current user
      * @var array
      */
-    private $_aUserGroups=array();
-    
+    private array $_aUserGroups = [];
+
     /**
      * list of roles based on the groups
      * @var array
      */
-    private $_aUserPermmissions=array();
-    
+    private array $_aUserPermmissions = [];
+
     /**
      * list of projects the current user is involved in
      * @var array
      */
-    private $_aProjects=array();
-    
+    private $_aProjects = [];
+
     /**
      * name of the last checked role
      * @var string
      */
-    private $_sLastCheckedPermission=false;
-    
+    private $_sLastCheckedPermission = false;
+
     /**
+     * Constructor
      * init user with optional given user
-     * @param type $sUser
+     * 
+     * @param string $sUser  username to set
      */
-    public function __construct($sUser=false){
+    public function __construct(string $sUser = '')
+    {
         $this->setUser($sUser);
     }
-    
-    
+
+
     // ----------------------------------------------------------------------
     // private functions
     // ----------------------------------------------------------------------
-    
-    
+
+
     /**
-     * get string with detected user from current session / basic auth / cli access
+     * Get string with detected user from current session / basic auth / cli access
+     * 
      * @return string
      */
-    private function _autoDetectUser(){
-        $sUser=false;
-        if (isset($_SESSION) && isset($_SESSION["PHP_AUTH_USER"])){
-            $sUser=$_SESSION["PHP_AUTH_USER"];
+    private function _autoDetectUser(): string
+    {
+        $sUser = '';
+        if (isset($_SESSION) && isset($_SESSION["PHP_AUTH_USER"])) {
+            $sUser = $_SESSION["PHP_AUTH_USER"];
         }
-        if (!$sUser && isset($_SERVER["PHP_AUTH_USER"])){
-            $sUser=$_SERVER["PHP_AUTH_USER"];
+        if (!$sUser && isset($_SERVER["PHP_AUTH_USER"])) {
+            $sUser = $_SERVER["PHP_AUTH_USER"];
         }
         if (php_sapi_name() == "cli") {
-            $sUser="cliadmin";
+            $sUser = "cliadmin";
         }
         return $sUser;
     }
 
-    // UNUSED SO FAR
-    private function _getUser2Projects(){
-        $sFile=__DIR__ . '/../../../config/inc_user2projects.php';
+    /**
+     * UNUSED SO FAR
+     * Idea: limit user access to a set of projects
+     */
+    private function _getUser2Projects()
+    {
+        $sFile = __DIR__ . '/../../../config/inc_user2projects.php';
         return file_exists($sFile)
             ? require $sFile
             : []
         ;
     }
-    
-    private function _getUser2Roles(){
-        $sFile=__DIR__ . '/../../../config/inc_user2roles.php';
+
+    /**
+     * Load roles per user from config
+     * @return array
+     */
+    private function _getUser2Roles(): array
+    {
+        $sFile = __DIR__ . '/../../../config/inc_user2roles.php';
         return file_exists($sFile)
             ? require $sFile
-            : ['admin'=>['admin']]
+            : ['admin' => ['admin']]
         ;
     }
+
     /**
      * TODO: reimplement
      * get the user groups of the current user from an internal source.
      * The function returns a flat aray with names of the groups
      * @return array
      */
-    private function _getUserGroups(){
-        $aGroups=array();
-        if ($this->_sUsername){
-            $aGroups[]="authenticated";
+    private function _getUserGroups(): array
+    {
+        $aGroups = [];
+        if ($this->_sUsername) {
+            $aGroups[] = "authenticated";
             // $aGroups[]='#'.$this->_sUsername;
-            $aUserDefinitions=$this->_getUser2Roles();
-            foreach (array_keys($aUserDefinitions) as $sGroup){
-                if (array_search($this->_sUsername, $aUserDefinitions[$sGroup])!==false){
-                    $aGroups[]=$sGroup;
+            $aUserDefinitions = $this->_getUser2Roles();
+            foreach (array_keys($aUserDefinitions) as $sGroup) {
+                if (array_search($this->_sUsername, $aUserDefinitions[$sGroup]) !== false) {
+                    $aGroups[] = $sGroup;
                 }
             }
         }
-        $this->_aUserGroups=$aGroups;
+        $this->_aUserGroups = $aGroups;
         return $this->_aUserGroups;
     }
 
@@ -114,66 +134,70 @@ class user {
      * The function returns a flat aray with names of the roles
      * @return array
      */
-    private function _getUserPermission(){
-        $aRoles=array();
-        $aRolesDefinitions=require(__DIR__ . '/../../../config/inc_roles.php');
+    private function _getUserPermission(): array
+    {
+        $aRoles = [];
+        $aRolesDefinitions = require(__DIR__ . '/../../../config/inc_roles.php');
 
         // anonymous roles:
-        $aRoles=array_merge($aRoles, $aRolesDefinitions['all']);
-        
-        foreach (array_keys($aRolesDefinitions) as $sGroup){
-            if ($this->hasGroup($sGroup)){
-                $aRoles=array_merge($aRoles, $aRolesDefinitions[$sGroup]);
+        $aRoles = array_merge($aRoles, $aRolesDefinitions['all']);
+
+        foreach (array_keys($aRolesDefinitions) as $sGroup) {
+            if ($this->hasGroup($sGroup)) {
+                $aRoles = array_merge($aRoles, $aRolesDefinitions[$sGroup]);
             }
         }
-        
-        $this->_aUserPermmissions=  array_unique($aRoles);
+
+        $this->_aUserPermmissions = array_unique($aRoles);
         return $this->_aUserPermmissions;
     }
-    
-    
+
+
     // ----------------------------------------------------------------------
     // public ACTIONS
     // ----------------------------------------------------------------------
-    
-    
+
+
     /**
      * authenticate a user with the configured methods
+     * 
      * @global array  $aConfig  global config
      * @global array  $aParams  params (i.e. GET and POST)
+     * 
      * @return boolean
      */
-    public function authenticate(){
+    public function authenticate(): bool
+    {
         global $aConfig, $aParams;
-        if(!isset($aConfig['auth']) || !is_array($aConfig['auth']) || !count($aConfig['auth']) || !isset($aParams['user'])){
+        if (!isset($aConfig['auth']) || !is_array($aConfig['auth']) || !count($aConfig['auth']) || !isset($aParams['user'])) {
             return false;
         }
-        $sUser=$aParams['user'];
-        $sPassword=isset($aParams['password']) ? $aParams['password'] : false;
+        $sUser = $aParams['user'];
+        $sPassword = isset($aParams['password']) ? $aParams['password'] : false;
 
-        foreach (array_keys($aConfig['auth']) as $sAuthMethod){
-            $oUserAuth=false;
-            switch ($sAuthMethod){
+        foreach (array_keys($aConfig['auth']) as $sAuthMethod) {
+            $oUserAuth = false;
+            switch ($sAuthMethod) {
                 case 'ldap':
                     require_once("userauth.ldap.class.php");
-                    $oUserAuth=new userauthLdap($aConfig['auth']['ldap']);
+                    $oUserAuth = new userauthLdap();
                     break;
                 // implement other methods here
                 // see userauth.ldap.class.php as simple example
-                    
+
                 default:
-                    echo 'WARNING: authmethod '.$sAuthMethod.' in your config is not implemented in '.basename(__FILE__).' and is useless so far.<br>';
+                    echo 'WARNING: authmethod ' . $sAuthMethod . ' in your config is not implemented in ' . basename(__FILE__) . ' and is useless so far.<br>';
             }
             // if authentication fails then continue and try next method
-            if ($oUserAuth && $oUserAuth->authenticate($sUser, $sPassword)){
-                
+            if ($oUserAuth && $oUserAuth->authenticate($sUser, $sPassword)) {
+
                 // set a session - it must correspondent with _autoDetectUser()
                 // $_SESSION["PHP_AUTH_USER"]=$sUser;
                 $this->setUser($sUser);
                 return true;
             }
             // if authentication fails then continue and try next method
-            if(!$oUserAuth){
+            if (!$oUserAuth) {
                 echo "DEBUG: ERROR oUserAuth waasn't initialized for [$sAuthMethod].<br>";
             }
         }
@@ -184,98 +208,116 @@ class user {
      * logoff user
      * @return boolean
      */
-    public function logoff(){
+    public function logoff(): bool
+    {
         unset($_SESSION["PHP_AUTH_USER"]);
         $this->setUser();
         return true;
     }
-    
+
     /**
      * set an authenticated user and get its roles
      * @param string  $sUser  optional: set a given username
+     * @return void
      */
-    public function setUser($sUser=false){
-        if($sUser!==false){
-            $this->_sUsername=$sUser;
-            $_SESSION["PHP_AUTH_USER"]=$sUser;
+    public function setUser(string $sUser = ''): void
+    {
+        if ($sUser) {
+            $this->_sUsername = $sUser;
+            $_SESSION["PHP_AUTH_USER"] = $sUser;
         } else {
             // check user from basic auth or cli
-            $this->_sUsername=$this->_autoDetectUser();
+            $this->_sUsername = $this->_autoDetectUser();
         }
         $this->_getUserGroups();
         $this->_getUserPermission();
     }
-    
+
     /**
-     * return html code to display a denied message
-     * @return type
+     * Get html code to display a denied message
+     * @return string
      */
-    public function showDenied(){
+    public function showDenied(): string
+    {
         return '<div class="alert alert-danger" role="alert">'
-        . ($this->_sUsername 
-            ? t("class-user-error-deny-no-role").'<br>'.$this->_sUsername.' --> ('.$this->_sLastCheckedPermission.')<br>'
-            : t("class-user-error-login-required")
-        )
-        . '</div><br>'
-        . '<a href="/deployment/all/login/" class="btn btn-primary">'.t('menu-login').'</a>'
+            . ($this->_sUsername
+                ? t("class-user-error-deny-no-role") . '<br>' . $this->_sUsername . ' --> (' . $this->_sLastCheckedPermission . ')<br>'
+                : t("class-user-error-login-required")
+            )
+            . '</div><br>'
+            . '<a href="/deployment/all/login/" class="btn btn-primary">' . t('menu-login') . '</a>'
         ;
     }
-    
+
     // ----------------------------------------------------------------------
     // public GETTER
     // ----------------------------------------------------------------------
 
-    
-    // UNUSED SO FAR
-    public function getUser2Projects(){
+
+    /**
+     * UNUSED SO FAR
+     * Idea: limit user access to a set of projects
+     */
+    public function getUser2Projects()
+    {
         return $this->_getUser2Projects();
     }
-    
-    public function getUser2Roles(){
+
+    /**
+     * Get a list of all roles for the current user
+     * @return array
+     */
+    public function getUser2Roles(): array
+    {
         return $this->_getUser2Roles();
     }
 
     /**
-     * get the current username
+     * Get the current username
      * @return string
      */
-    public function getUsername(){
+    public function getUsername(): string
+    {
         return $this->_sUsername;
     }
+
     /**
-     * get a flat array with roles of the current user
+     * Get a flat array with roles of the current user
      * @return array
      */
-    public function getUserGroups(){
+    public function getUserGroups(): array
+    {
         return $this->_aUserGroups;
     }
     /**
-     * get a flat array with roles of the current user
+     * Get a flat array with roles of the current user
      * @return array
      */
-    public function getUserPermission(){
+    public function getUserPermission(): array
+    {
         return $this->_aUserPermmissions;
     }
 
     /**
      * check if the current user has a given role name
      * @param string  $sGroupname  name of the role to check
-     * @return type
+     * @return bool
      */
-    public function hasGroup($sGroupname){
-        return (array_search($sGroupname, $this->_aUserGroups)!==false);
+    public function hasGroup($sGroupname)
+    {
+        return !!(array_search($sGroupname, $this->_aUserGroups) !== false);
     }
     /**
      * check if the current user has a given role name
      * @param string  $sPermission  name of the role to check
-     * @return type
+     * @return boolean
      */
-    public function hasPermission($sPermission){
-        $this->_sLastCheckedPermission=$sPermission;
-        $bReturn=array_search($sPermission, $this->_aUserPermmissions)!==false;
+    public function hasPermission($sPermission): bool
+    {
+        $this->_sLastCheckedPermission = $sPermission;
+        $bReturn = !!(array_search($sPermission, $this->_aUserPermmissions) !== false);
         // $this->log(__FUNCTION__ . "($sRolename) -> " . $bReturn ? 'true' : 'false');
         return $bReturn;
     }
 
-    
 }
diff --git a/public_html/deployment/classes/userauth.interface.php b/public_html/deployment/classes/userauth.interface.php
index 83a4a3acb9c1e5dab5938aa4ea8a39b66a05a19c..3bd5c8372f6e6e2ad1e560629ab5f13c7e170826 100644
--- a/public_html/deployment/classes/userauth.interface.php
+++ b/public_html/deployment/classes/userauth.interface.php
@@ -1,14 +1,23 @@
 <?php
 /**
  * interface for user authentication
+ * 
  * @author axel.hahn@iml.unibe.ch
+ * 
+ * Axel <axel.hahn@unibe.ch>
+ * 2024-08-29  Axel php8 only; added variable types; use short array syntax
  */
-interface iUserAuth {
+interface iUserAuth
+{
 
     /**
-     * verify if a given user and password combination is correct
+     * Verify if a given user and password combination is correct
+     * 
+     * @param string  $sUser      username
+     * @param string  $sPassword  password
+     * @return boolean
      */
-    public function authenticate($sUser, $sPassword);
-    
+    public function authenticate(string $sUser, string $sPassword): bool;
+
 
 }
\ No newline at end of file
diff --git a/public_html/deployment/classes/userauth.ldap.class.php b/public_html/deployment/classes/userauth.ldap.class.php
index 07c440ad5dcdba27fd6b7fe1548f018b78da33f2..a8e5bc1cc88d1e0b11b0db02df591249b3debdb4 100644
--- a/public_html/deployment/classes/userauth.ldap.class.php
+++ b/public_html/deployment/classes/userauth.ldap.class.php
@@ -8,42 +8,58 @@ require_once("ldap.class.php");
  * implements userauth interface
  * 
  * @author hahn
+ * 
+ * Axel <axel.hahn@unibe.ch>
+ * 2024-08-29  Axel php8 only; added variable types
  */
-class userauthLdap implements iUserAuth {
+class userauthLdap implements iUserAuth
+{
 
     /**
      * object for ldap actions
      * @var object
      */
-    private $_oLdap=false;
-    
+    private object $_oLdap;
+
     // ----------------------------------------------------------------------
     // 
     // ----------------------------------------------------------------------
-    public function __construct() {
+
+    /**
+     * Constructor
+     */
+    public function __construct()
+    {
         global $aConfig;
-        $this->_oLdap=new imlldap($aConfig['auth']['ldap']);
-        
+        $this->_oLdap = new imlldap($aConfig['auth']['ldap']);
+
         // first test of ldap connection
         // $this->_oLdap->debugOn();
         $this->_oLdap->connect();
-        return true;
     }
-    
-    public function __destruct() {
+
+    /**
+     * Destructor
+     * Close ldap connection
+     */
+    public function __destruct()
+    {
         $this->_oLdap->close();
     }
-    
+
     // ----------------------------------------------------------------------
     // implementation
     // ----------------------------------------------------------------------
+
     /**
-     * verify if a given user and password combination is correct
-     * @param string   $sUser      username
-     * @param password $sPassword  password
+     * Verify if a given user and password combination is correct
+     * 
+     * @param string  $sUser      username
+     * @param string  $sPassword  password
      * @return boolean
      */
-    public function authenticate($sUser, $sPassword){
+    public function authenticate(string $sUser, string $sPassword): bool
+    {
         return $this->_oLdap->verifyPassword($sUser, $sPassword);
     }
 
diff --git a/public_html/deployment/classes/vcs.git.class.php b/public_html/deployment/classes/vcs.git.class.php
index 312f957b56900a16797853c5d064004cfe780ed9..dc16c3dd676f85c7aa091e12171eb26ab23eabc9 100644
--- a/public_html/deployment/classes/vcs.git.class.php
+++ b/public_html/deployment/classes/vcs.git.class.php
@@ -1,7 +1,7 @@
 <?php
 
 require_once("vcs.interface.php");
-require_once __DIR__.'/../../vendor/axelhahn/ahcache/cache.class.php';
+require_once __DIR__ . '/../../vendor/axelhahn/ahcache/cache.class.php';
 
 /**
  * version control system :: GIT
@@ -9,77 +9,88 @@ require_once __DIR__.'/../../vendor/axelhahn/ahcache/cache.class.php';
  * 
  *
  * @author hahn
+ * 
+ * Axel: <axel.hahn@unibe.ch>
+ * (...)
+ * 2024-08-28  Axel   php8 only; added variable types; short array syntax
+
  */
-class vcs implements iVcs {
-// class vcs {
+class vcs implements iVcs
+{
+    // class vcs {
 
     /**
      * configuration
      * @var array 
      */
-    private $_aCfg = array();
+    private array $_aCfg = [];
 
     /**
      * temp dir to fetch repo version and ommit message; its value will be
      * generated in set_config()
      * @var string
      */
-    private $_sTempDir = false;  // 
+    private string $_sTempDir = '';  // 
 
     /**
      * filename of ssh key file with complete path
      * @var string
      */
-    private $_sKeyfile = false;
+    private string $_sKeyfile = '';
 
     /**
      * filename of ssh wrapper script with complete path
      * @var string
      */
-    private $_sWrapper = false;
+    private string $_sWrapper = '';
 
     /**
      * flat array with remote branch names
      * @var array
      */
-    private $_aRemoteBranches = array();
+    private array $_aRemoteBranches = [];
 
     /**
      * name of the default remote branch to access
      * @var string
      */
-    private $_sCurrentBranch = "";
+    private string $_sCurrentBranch = '';
 
     /**
-     * constructor
+     * Constructor
      * @param array $aRepoConfig
      */
-    public function __construct($aRepoConfig = array()) {
+    public function __construct(array $aRepoConfig = [])
+    {
         $this->setConfig($aRepoConfig);
         $this->getRemoteBranches(); // to fill the cache
     }
-    
+
     /**
-     * add a log messsage
+     * Add a log messsage
      * @global object $oLog
+     * 
      * @param  string $sMessage  messeage text
      * @param  string $sLevel    warnlevel of the given message
      * @return bool
      */
-    private function log($sMessage,$sLevel="info"){
+    private function log(string $sMessage, string $sLevel = "info"): bool
+    {
         global $oCLog;
-        return $oCLog->add(basename(__FILE__)." class ".__CLASS__." - ".$sMessage,$sLevel);
+        return $oCLog->add(basename(__FILE__) . " class " . __CLASS__ . " - " . $sMessage, $sLevel);
     }
 
     /**
-     * set a config and update internal (private) variables
+     * Set a config and update internal (private) variables
+     * 
      * @param array $aRepoConfig
      * @return boolean
      */
-    public function setConfig($aRepoConfig = array()) {
+    public function setConfig(array $aRepoConfig = []): bool
+    {
         // checks
-        // foreach (array("type", "url") as $key) {
-        foreach (array("type") as $key) {
+        // foreach (["type", "url"] as $key) {
+        foreach (["type"] as $key) {
             if (!isset($aRepoConfig[$key])) {
                 die("ERROR: key $key does not exist in config <pre>" . print_r($aRepoConfig, true) . "</pre>");
             }
@@ -99,50 +110,56 @@ class vcs implements iVcs {
         $this->_sWrapper = $this->_aCfg["appRootDir"] . "/shellscripts/gitsshwrapper.sh";
         $this->_setTempdir();
 
-        return $this->_aCfg = $aRepoConfig;
+        $this->_aCfg = $aRepoConfig;
+        return true;
     }
 
     /**
-     * set directory für current branch of a project below tempdir
-     * In it the branch will be initialized
-     * @return type
+     * Get directory für current branch of a project below tempdir
+     * If it does not exist yet it will be created and the repository will be initialized.
+     * 
+     * @return string
      */
-    private function _setTempdir() {
+    private function _setTempdir(): string
+    {
         $this->_sTempDir = $this->_aCfg["url"];
         $this->_sTempDir = preg_replace('/[\@\.\:\/]/', '_', $this->_sTempDir);
         $this->_sTempDir = $this->_aCfg["tmpDir"] . '/checkout_vcsgit_' . $this->_sTempDir . '/';
-        $this->_sTempDir .= preg_replace('/[\@\.\:\/]/', '_', ($this->_sCurrentBranch ? $this->_sCurrentBranch : '__no-branch__') ) . '/';
+        $this->_sTempDir .= preg_replace('/[\@\.\:\/]/', '_', ($this->_sCurrentBranch ? $this->_sCurrentBranch : '__no-branch__')) . '/';
 
-        if (!is_dir($this->_sTempDir . ".git") ) {
-            $this->log(__FUNCTION__." does not exist yet: " . $this->_sTempDir . ".git");
+        if (!is_dir($this->_sTempDir . ".git")) {
+            $this->log(__FUNCTION__ . " does not exist yet: " . $this->_sTempDir . ".git");
             $sGitCmd = 'export GIT_SSH="' . $this->_sWrapper . '" ; export PKEY="' . $this->_sKeyfile . '" ; ';
-            $sGitCmd.='mkdir -p "' . $this->_sTempDir . '" && cd "' . $this->_sTempDir . '" && ';
-            $sGitCmd.='git init >/dev/null && ';
-            $sGitCmd.='git remote add origin "' . $this->getUrl() . '" 2>&1 ';
+            $sGitCmd .= 'mkdir -p "' . $this->_sTempDir . '" && cd "' . $this->_sTempDir . '" && ';
+            $sGitCmd .= 'git init >/dev/null && ';
+            $sGitCmd .= 'git remote add origin "' . $this->getUrl() . '" 2>&1 ';
             // $sGitCmd='time ('.$sGitCmd.')';
             // exec($sGitCmd, $aOutput, $iRc);
-            $this->log(__FUNCTION__." start command <code>$sGitCmd</code>");
-            exec($sGitCmd, $aOutput,$iRc);
-            $this->log(__FUNCTION__." command ended with rc=$iRc ". '<pre>'.implode("\n", $aOutput).'</pre>', ($iRc==0 ? 'info':'error'));
+            $this->log(__FUNCTION__ . " start command <code>$sGitCmd</code>");
+            exec($sGitCmd, $aOutput, $iRc);
+            $this->log(__FUNCTION__ . " command ended with rc=$iRc " . '<pre>' . implode("\n", $aOutput) . '</pre>', ($iRc == 0 ? 'info' : 'error'));
         }
         return $this->_sTempDir;
     }
 
     /**
-     * set the current branch
+     * Set the current branch
+     * 
      * @param string $sBranchname  name of the branch
+     * @return void
      */
-    public function setCurrentBranch($sBranchname) {
+    public function setCurrentBranch($sBranchname): void
+    {
         $this->_sCurrentBranch = $sBranchname;
         $this->_setTempdir();
-        return $this->_sCurrentBranch;
     }
 
     /**
      * helper: dump values
      * @return boolean
      */
-    public function dump() {
+    public function dump(): bool
+    {
         echo "<h3>Dump class " . __CLASS__ . "</h3>";
         echo "config array: <pre>" . print_r($this->_aCfg, true) . "</pre>";
         echo "temp dir to read revision: " . $this->_sTempDir . "<br>";
@@ -151,10 +168,14 @@ class vcs implements iVcs {
 
     /**
      * cleanup unneeded files and directories in a checked out directory
-     * and remove all vcs specific files and directories
+     * and remove all vcs specific files and directories.
+     * This method works on linux only
+     * 
+     * @param string $sWorkDir  path of the build directory to cleanup the git meta data from
      * @return bool
      */
-    public function cleanupWorkdir($sWorkDir) {
+    public function cleanupWorkdir(string $sWorkDir): bool
+    {
         if (!is_dir($sWorkDir)) {
             return false;
         }
@@ -164,32 +185,36 @@ class vcs implements iVcs {
     }
 
     /**
-     * get the current branch
+     * Get the current branch
+     * 
      * @param bool  $bReturnMasterIfEmpty  flag: if there is no current branch then detect a master branch
      * @return string
      */
-    public function getCurrentBranch($bReturnMasterIfEmpty=false) {
-        if(!$this->_sCurrentBranch){
-            if ($bReturnMasterIfEmpty){
-                $this->_sCurrentBranch=$this->_getMasterbranchname();
+    public function getCurrentBranch(bool $bReturnMasterIfEmpty = false): string
+    {
+        if (!$this->_sCurrentBranch) {
+            if ($bReturnMasterIfEmpty) {
+                $this->_sCurrentBranch = $this->_getMasterbranchname();
             }
         }
         return $this->_sCurrentBranch;
     }
 
     /**
-     * detect an existing master branch ... and return one of 'origin/main' | 'origin/master'
+     * Detect an existing master branch ... and return one of 'origin/main' | 'origin/master'
+     * 
      * @return string
      */
-    protected function _getMasterbranchname(){
-        $sMasterBranch='';
-        $aMasternames=['origin/main', 'origin/master'];
-
-        $aAllBranches=$this->getRemoteBranches();
-        if(count($aAllBranches)){
-            foreach($aMasternames as $sBranchToTest){
-                if (isset($aAllBranches[$sBranchToTest])){
-                    $sMasterBranch=$sBranchToTest;
+    protected function _getMasterbranchname(): string
+    {
+        $sMasterBranch = '';
+        $aMasternames = ['origin/main', 'origin/master'];
+
+        $aAllBranches = $this->getRemoteBranches();
+        if (count($aAllBranches)) {
+            foreach ($aMasternames as $sBranchToTest) {
+                if (isset($aAllBranches[$sBranchToTest])) {
+                    $sMasterBranch = $sBranchToTest;
                     break;
                 }
             }
@@ -198,98 +223,109 @@ class vcs implements iVcs {
     }
 
     /**
-     * return the build type, i.e. git|svn|cvs|
+     * Get the build type, i.e. git|svn|cvs|
      * @return string
      */
-    public function getBuildType() {
-        return $this->_aCfg["type"];
+    public function getBuildType(): string
+    {
+        return $this->_aCfg["type"] ?? '';
     }
 
     /**
-     * get a nice name for a cache module based on repo url
-     * @return type
+     * Get a nice name for a cache module based on repo url
+     * 
+     * @return string
      */
-    private function _getNameOfCacheModule() {
+    private function _getNameOfCacheModule(): string
+    {
         return preg_replace('/([^0-9a-z])/i', "", $this->getUrl());
     }
 
     /**
-     * cleanup cache data for this project (revisions, list of branches+tags)
+     * Cleanup cache data for this project (revisions, list of branches+tags)
+     * 
+     * @param int $iAge  max age in sec; older items will be deleted
      * @return bool
      */
-    public function cleanupCache($iAge) {
-        $this->log(__FUNCTION__." start");
+    public function cleanupCache(int $iAge): string
+    {
+        $this->log(__FUNCTION__ . " start");
         $oCache = new AhCache($this->_getNameOfCacheModule());
         ob_start();
-        $oCache->cleanup((int)$iAge);
+        $oCache->cleanup((int) $iAge);
         $sOut = ob_get_contents();
         ob_end_clean();
         return $sOut;
     }
 
     /**
-     * helper: cache hash with all branches
+     * helper: store hash with all branches in cache
      * It saves 1.5 sec for reading 300 branches
+     * 
      * @return boolean
      */
-    private function _cacheRemoteBranches() {
-        $iTtl=300;
+    private function _cacheRemoteBranches(): bool
+    {
+        $iTtl = 300;
         $oCache = new AhCache($this->_getNameOfCacheModule(), "RemoteBranches");
         // $this->log(__FUNCTION__." <pre>".print_r($this->_aRemoteBranches, 1)."</pre>");
         $oCache->write($this->_aRemoteBranches, $iTtl);
         return true;
     }
-    
+
     /**
-     * read remote repository and get an array with names and revisions of 
+     * Read remote repository and get an array with names and revisions of 
      * all branches and tags
-     * pre branch you get an array element with the keys revision, name, type
+     * per branch you get an array element with the keys revision, name, type
+     * It returns false if there is no git url
+     * 
      * @param bool $bIgnoreCache  flag to overrde cache
      * @return array
      */
-    private function _fetchRemoteBranches($bIgnoreCache = false) {
-        $this->log(__FUNCTION__."(bIgnoreCache = ".($bIgnoreCache ? 'true' : 'false').") start");
-        $aReturn = array();
+    private function _fetchRemoteBranches($bIgnoreCache = false): bool|array
+    {
+        $this->log(__FUNCTION__ . "(bIgnoreCache = " . ($bIgnoreCache ? 'true' : 'false') . ") start");
+        $aReturn = [];
 
-        $sGitUrl=$this->getUrl();
+        $sGitUrl = $this->getUrl();
         if (!$sGitUrl) {
             return false;
         }
 
         $oCache = new AhCache($this->_getNameOfCacheModule(), "RemoteBranches");
-        $aOutput=[]; 
-        $iRc=false;
+        $aOutput = [];
+        $iRc = false;
 
         // list of cached branch keys
         if ($oCache->isExpired() || $bIgnoreCache) {
             // workdir is on level of set project ... going 1 level up means to leave the dir with the current branch
             $sWorkdir = dirname($this->_sTempDir) . '/fetchRemoteBranches/';
-            $this->log(__FUNCTION__." - sWorkdir = $sWorkdir");
+            $this->log(__FUNCTION__ . " - sWorkdir = $sWorkdir");
             $sGitCmd = 'export GIT_SSH="' . $this->_sWrapper . '" ; export PKEY="' . $this->_sKeyfile . '" ; ';
-            
+
             if (is_dir($sWorkdir . ".git")) {
                 // if a subdir .git exists:
                 // Verify if git remote -v contains the current git url
                 // If not, we delete it
-                $sPreCmd='cd "' . $sWorkdir . '" 2>&1 && git remote -v 2>&1 | grep -F "' . $sGitUrl . '" >/dev/null || ( echo "DELETING .git dir..."; rm -rf .git && rc=$?; echo "rc=$rc"; sleep 1; exit $rc) ';
-                $this->log(__FUNCTION__." - start PRE command <code>$sPreCmd</code>");
+                $sPreCmd = 'cd "' . $sWorkdir . '" 2>&1 && git remote -v 2>&1 | grep -F "' . $sGitUrl . '" >/dev/null || ( echo "DELETING .git dir..."; rm -rf .git && rc=$?; echo "rc=$rc"; sleep 1; exit $rc) ';
+                $this->log(__FUNCTION__ . " - start PRE command <code>$sPreCmd</code>");
                 exec($sPreCmd, $aPreLines, $iRc);
-                if (!$iRc==0){
-                    $this->log(__FUNCTION__." <code>".print_r($aPreLines, 1)."</code> rc=$iRc");
+                if (!$iRc == 0) {
+                    $this->log(__FUNCTION__ . " <code>" . print_r($aPreLines, 1) . "</code> rc=$iRc");
                 }
             }
 
             if (!is_dir($sWorkdir . ".git")) {
-                $sGitCmd.='mkdir -p "' . $sWorkdir . '" && cd "' . $sWorkdir . '" && ';
-                $sGitCmd.='git init >/dev/null && ';
-                $sGitCmd.='git remote add origin "' . $sGitUrl . '" 2>&1 && ';
+                $sGitCmd .= 'mkdir -p "' . $sWorkdir . '" && cd "' . $sWorkdir . '" && ';
+                $sGitCmd .= 'git init >/dev/null && ';
+                $sGitCmd .= 'git remote add origin "' . $sGitUrl . '" 2>&1 && ';
             } else {
-                $sGitCmd.='cd "' . $sWorkdir . '" 2>&1 && ';
+                $sGitCmd .= 'cd "' . $sWorkdir . '" 2>&1 && ';
             }
-            $sGitCmd.='git ls-remote --heads --tags origin 2>&1 ;';
-            $this->log(__FUNCTION__." - start command <code>$sGitCmd</code>");
+            $sGitCmd .= 'git ls-remote --heads --tags origin 2>&1 ;';
+            $this->log(__FUNCTION__ . " - start command <code>$sGitCmd</code>");
             exec($sGitCmd, $aOutput, $iRc);
-            $this->log(__FUNCTION__." - command ended with rc=$iRc ". '<pre>'.implode("\n", $aOutput).'</pre>', ($iRc==0 ? 'info':'error'));
+            $this->log(__FUNCTION__ . " - command ended with rc=$iRc " . '<pre>' . implode("\n", $aOutput) . '</pre>', ($iRc == 0 ? 'info' : 'error'));
             if ($iRc == 0) {
 
                 $this->log(__FUNCTION__ . ' start reading all branches');
@@ -314,14 +350,14 @@ class vcs implements iVcs {
                     // http://stackoverflow.com/questions/15472107/when-listing-git-ls-remote-why-theres-after-the-tag-name
                     if (!preg_match('/\^\{\}$/', $sBranch)) {
                         $sRevision = $aTmp[0];
-                        $sType   = preg_replace('#/.*$#',      '', $sBranchPath);
+                        $sType = preg_replace('#/.*$#', '', $sBranchPath);
                         $sName = ($sType == "heads") ? "origin/" . $sBranch : $sBranch;
-                        
+
                         $sBranchKey = $sName;
                         // $this->log(__FUNCTION__ . ' $sBranchKey = '.$sBranchKey);
 
                         // $sMessage = $this->getCommitmessageByBranch($sName, $sRevision);
-                        $aReturn[$sBranchKey] = array(
+                        $aReturn[$sBranchKey] = [
                             // 'debug'=> $aTmp,
                             'revision' => $sRevision,
                             'name' => $sName,
@@ -329,15 +365,15 @@ class vcs implements iVcs {
                             'label' => $sType . ': ' . $sBranch,
                             'type' => $sType,
                             // 'message' => $sMessage
-                        );
+                        ];
                     }
                 }
                 $this->_aRemoteBranches = $aReturn;
-                $this->log(__FUNCTION__ . ' '.count($aReturn).' branches: <pre>'.print_r($this->_aRemoteBranches, 1).'</pre>');
+                $this->log(__FUNCTION__ . ' ' . count($aReturn) . ' branches: <pre>' . print_r($this->_aRemoteBranches, 1) . '</pre>');
                 $this->_cacheRemoteBranches();
             } else {
                 // $this->_aRemoteBranches = $oCache->read();
-                $this->log(__FUNCTION__." - No git access? --> deleting cache of former fetched branches...");
+                $this->log(__FUNCTION__ . " - No git access? --> deleting cache of former fetched branches...");
                 $oCache->delete();
                 $this->_aRemoteBranches = [];
             }
@@ -349,42 +385,46 @@ class vcs implements iVcs {
     }
 
     /**
-     * get a flat array with names of all remote branches
+     * Get a flat array with names of all remote branches
      * @param  bool  $bIgnoreCache  flag: ignore caching; default: use cache
      * @return array
      */
-    public function getRemoteBranches($bIgnoreCache=false) {
-        $this->log(__FUNCTION__."($bIgnoreCache) start");
+    public function getRemoteBranches(bool $bIgnoreCache = false): array
+    {
+        $this->log(__FUNCTION__ . "($bIgnoreCache) start");
         if (!$this->_aRemoteBranches || $bIgnoreCache) {
-            $this->log(__FUNCTION__."($bIgnoreCache) --> fetching fresh data");
+            $this->log(__FUNCTION__ . "($bIgnoreCache) --> fetching fresh data");
             $this->_fetchRemoteBranches($bIgnoreCache);
         } else {
-            $this->log(__FUNCTION__."($bIgnoreCache) --> returning cached data");
+            $this->log(__FUNCTION__ . "($bIgnoreCache) --> returning cached data");
         }
         return $this->_aRemoteBranches;
     }
 
     /**
-     * get current revision and commit message from remote repository
+     * Get current revision and commit message from remote repository
      * @see $this::getRevision
+     * It returns false if no branch is set
+     * 
      * @param boolean  $bRefresh  optional: refresh data; default: use cache
-     * @return array
+     * @return bool|array
      */
-    public function getRepoRevision($bRefresh=false) {
-        $this->log(__FUNCTION__."($bRefresh) start");
-        if(!$this->_sCurrentBranch){
+    public function getRepoRevision(bool $bRefresh = false): bool|array
+    {
+        $this->log(__FUNCTION__ . "($bRefresh) start");
+        if (!$this->_sCurrentBranch) {
             return false;
         }
         $sMessage = $this->getCommitmessageByBranch(false, $bRefresh ? 'dummy_to_force_refresh' : false);
         if ($sMessage) {
-            $aReturn = array(
+            $aReturn = [
                 'branch' => $this->_sCurrentBranch,
                 'shortname' => $this->_aRemoteBranches[$this->_sCurrentBranch]['shortname'],
                 'revision' => $this->_aRemoteBranches[$this->_sCurrentBranch]['revision'],
                 'type' => $this->_aRemoteBranches[$this->_sCurrentBranch]['type'],
                 'message' => $sMessage,
-                '_data'=>$this->_aRemoteBranches[$this->_sCurrentBranch],
-            );
+                '_data' => $this->_aRemoteBranches[$this->_sCurrentBranch],
+            ];
         } else {
             $aReturn = $this->getRevision(false);
         }
@@ -392,31 +432,35 @@ class vcs implements iVcs {
     }
 
     /**
-     * get a commit message of a given branch
+     * Get a commit message of a given branch.
+     * It reurns if no branch was found in meta infos
+     * 
      * @param string  $sBranch          name of a branch
      * @param string  $sVerifyRevision  optional: revision to verify if it is the newsest
      * @return string
      */
-    public function getCommitmessageByBranch($sBranch = false, $sVerifyRevision = false) {
-        $this->log(__FUNCTION__."($sBranch, $sVerifyRevision) start");
+    public function getCommitmessageByBranch(string $sBranch = '', string $sVerifyRevision = ''): bool|string
+    {
+        $this->log(__FUNCTION__ . "($sBranch, $sVerifyRevision) start");
         if (!$sBranch) {
             $sBranch = $this->_sCurrentBranch;
         }
         // try to get infos from the cache
         if (
-                is_array($this->_aRemoteBranches)
-                && (
-                   isset($this->_aRemoteBranches[$sBranch]) && $sVerifyRevision && $this->_aRemoteBranches[$sBranch]['revision'] == $sVerifyRevision
-                   ||
-                   isset($this->_aRemoteBranches[$sBranch]) && !$sVerifyRevision
-                )
+            is_array($this->_aRemoteBranches)
+            && (
+                isset($this->_aRemoteBranches[$sBranch]) && $sVerifyRevision && $this->_aRemoteBranches[$sBranch]['revision'] == $sVerifyRevision
+                ||
+                isset($this->_aRemoteBranches[$sBranch]) && !$sVerifyRevision
+            )
+            && isset($this->_aRemoteBranches[$sBranch]['message'])
         ) {
             // it is up to date - doing nothing
-            $this->log(__FUNCTION__." return cached data");
+            $this->log(__FUNCTION__ . " return cached data");
             return $this->_aRemoteBranches[$sBranch]['message'];
         }
         // ok, then I need to read it
-        $this->log(__FUNCTION__." return fresh data");
+        $this->log(__FUNCTION__ . " return fresh data");
         if ($this->_sCurrentBranch != $sBranch) {
             $sSaveBranch = $this->_sCurrentBranch;
             $this->setCurrentBranch($sBranch);
@@ -425,14 +469,14 @@ class vcs implements iVcs {
         } else {
             $a = $this->getRevision(false);
         }
-        if(!isset($a['branch'])){
+        if (!isset($a['branch'])) {
             return false;
         }
         // merge with cached info ... to add type and label
-        if(isset($this->_aRemoteBranches[$a['branch']])){
-            $this->_aRemoteBranches[$a['branch']]=array_merge($this->_aRemoteBranches[$a['branch']], $a);
+        if (isset($this->_aRemoteBranches[$a['branch']])) {
+            $this->_aRemoteBranches[$a['branch']] = array_merge($this->_aRemoteBranches[$a['branch']], $a);
         } else {
-            $this->_aRemoteBranches[$a['branch']]=$a;
+            $this->_aRemoteBranches[$a['branch']] = $a;
         }
         // store in cache
         $this->_cacheRemoteBranches();
@@ -440,29 +484,31 @@ class vcs implements iVcs {
     }
 
     /**
-     * get current revision and commit message from an existing directory or a
+     * Get current revision and commit message from an existing directory or a
      * remote repository
      * the return will fill $this->_aData["phases"]["source"] in project class
      * (array keys are revision, message or error)
      *    if ok:
-     *       array(
+     *       [
      *          "branch" => $sRevision,
      *          "revision" => $sRevision,
      *          "message" => $sCommitmessage
-     *      );
+     *      ];
      *   
      *  on error:
-     *      array(
+     *      [
      *          "error" => $sErrormessage,
-     *      );
+     *      ];
+     * 
      * @param string  $sWorkDir  optional: local directory with initialized git repo
-     * @return array
+     * @return bool|array
      */
-    public function getRevision($sWorkDir = false) {
-        $this->log(__FUNCTION__." start");
-        $aReturn = array();
-        $aOutput=array();
-        $iRc=false;
+    public function getRevision(string $sWorkDir = ''): bool|array
+    {
+        $this->log(__FUNCTION__ . " start");
+        $aReturn = [];
+        $aOutput = [];
+        $iRc = false;
         if (!$this->getUrl()) {
             return false;
         }
@@ -477,33 +523,33 @@ class vcs implements iVcs {
         $sGitCmd.='git clone -b '.$this->_sCurrentBranch.' --single-branch '.$this->getUrl().' --depth 1 --bare "' . $sWorkDir . '" 2>&1; rm -rf "' . $sWorkDir . '"';
         */
         if ($sWorkDir) {
-            $sGitCmd.='cd "' . $sWorkDir . '" && ';
+            $sGitCmd .= 'cd "' . $sWorkDir . '" && ';
         } else {
             if (!file_exists($this->_sTempDir . ".git")) {
-                $this->log(__FUNCTION__." does not exist yet: ".$this->_sTempDir . ".git");
-                $sGitCmd.='mkdir -p "' . $this->_sTempDir . '" && cd "' . $this->_sTempDir . '" && ';
-                $sGitCmd.='git init >/dev/null 2>&1 && ';
-                $sGitCmd.='git remote add origin "' . $this->getUrl() . '" 2>&1 && ';
+                $this->log(__FUNCTION__ . " does not exist yet: " . $this->_sTempDir . ".git");
+                $sGitCmd .= 'mkdir -p "' . $this->_sTempDir . '" && cd "' . $this->_sTempDir . '" && ';
+                $sGitCmd .= 'git init >/dev/null 2>&1 && ';
+                $sGitCmd .= 'git remote add origin "' . $this->getUrl() . '" 2>&1 && ';
             } else {
-                $sGitCmd.='cd "' . $this->_sTempDir . '" && ';
+                $sGitCmd .= 'cd "' . $this->_sTempDir . '" && ';
             }
 
             // TODO: git 1.9 does needs only the line with --tags
-            $sGitCmd.=' ( '
-                    // . 'git fetch --update-head-ok --tags --depth 1 2>&1 ; ' // 1.5 s
-                    . 'git fetch --update-head-ok --tags --depth 1 2>&1 ; ' // 1.5 s
-                    //. 'git fetch --update-head-ok --depth 1 2>&1 '          // 1.5 s
-                    . ') && ';
+            $sGitCmd .= ' ( '
+                // . 'git fetch --update-head-ok --tags --depth 1 2>&1 ; ' // 1.5 s
+                . 'git fetch --update-head-ok --tags --depth 1 2>&1 ; ' // 1.5 s
+                //. 'git fetch --update-head-ok --depth 1 2>&1 '          // 1.5 s
+                . ') && ';
         }
 
-        $sGitCmd.='git log -1 "' . $this->_sCurrentBranch . '" 2>&1 ; '; // 0.0 s
+        $sGitCmd .= 'git log -1 "' . $this->_sCurrentBranch . '" 2>&1 ; '; // 0.0 s
         // $sGitCmd.='git log -1  2>&1 ; '; // 0.0 s
-        $this->log(__FUNCTION__." start command <code>$sGitCmd</code>");
+        $this->log(__FUNCTION__ . " start command <code>$sGitCmd</code>");
         // $sLoginfo = shell_exec($sGitCmd);
         exec($sGitCmd, $aOutput, $iRc);
-        $this->log(__FUNCTION__." command ended with rc=$iRc ". '<pre>'.implode("\n", $aOutput).'</pre>', ($iRc==0 ? 'info':'error'));
-        $sLoginfo= implode("\n", $aOutput);
-        
+        $this->log(__FUNCTION__ . " command ended with rc=$iRc " . '<pre>' . implode("\n", $aOutput) . '</pre>', ($iRc == 0 ? 'info' : 'error'));
+        $sLoginfo = implode("\n", $aOutput);
+
         /*
          * 
          * example output:
@@ -521,7 +567,7 @@ class vcs implements iVcs {
 
         // parse revision
         $sRevision = false;
-        $aRev=array();
+        $aRev = [];
         if (preg_match('#commit\ (.*)#', $sLoginfo, $aRev)) {
             $sRevision = $aRev[1];
         }
@@ -536,30 +582,35 @@ class vcs implements iVcs {
             // $sCommitMsg=preg_replace('/Author:\ .*\n/', '', $sCommitMsg);
             // $sCommitMsg=preg_replace('/Date:\ .*\n/', '', $sCommitMsg);
 
-            $aReturn = array(
+            $aReturn = [
                 "branch" => $this->_sCurrentBranch,
                 "revision" => $sRevision,
                 "message" => $sCommitMsg // ."\n". microtime(true),
-            );
+            ];
         } else {
             if (!$sLoginfo) {
                 $sLoginfo = $sGitCmd;
             }
             // echo "DEBUG: error on reading git revision<br>";
-            $aReturn = array(
+            $aReturn = [
                 "error" => '<pre>' . $sLoginfo . '<hr>' . $sGitCmd . '</pre>'
-            );
+            ];
         }
         // $this->log(__FUNCTION__ . ' return is <pre>'.print_r($aReturn, 1).'</pre>');
         return $aReturn;
     }
 
     /**
-     * get sources from vsc and check them out in given directory
-     * @return bool
+     * Get sources from vsc and check them out in given directory
+     * It returns false if the workdir was not found or the url for git repo is not set
+     * Otherwise it retunrs the output of git clone + git checkout
+     * 
+     * @param string $sWorkDir working dir where to check out source url
+     * @return bool|string
      */
-    public function getSources($sWorkDir) {
-        $this->log(__FUNCTION__." start");
+    public function getSources(string $sWorkDir): bool|string
+    {
+        $this->log(__FUNCTION__ . " start");
         if (!$sWorkDir || !is_dir($sWorkDir)) {
             return false;
         }
@@ -569,36 +620,42 @@ class vcs implements iVcs {
         $sBranchname = str_replace("origin/", "", $this->_sCurrentBranch);
 
         $sGitCmd = 'export GIT_SSH="' . $this->_sWrapper . '" ; export PKEY="' . $this->_sKeyfile . '" ; ';
-        $aOutput=[];
-        $iRc=false;
-        
+        $aOutput = [];
+        $iRc = false;
+
         // this does not checkout tags in git v1.7 - only branches:
         // $sGitCmd .= 'echo git clone --depth 1 --recursive --branch "' . $sBranchname . '" "' . $this->getUrl() . '" "' . $sWorkDir . '" ; ';
         // $sGitCmd .= '     git clone --depth 1 --recursive --branch "' . $sBranchname . '" "' . $this->getUrl() . '" "' . $sWorkDir . '" 2>&1; ';
         // 
-        $sGitCmd .= 'echo git clone "' . $this->getUrl() . '" "'.$sWorkDir.'" 2>&1 \&\& cd  "'.$sWorkDir.'" \&\& git checkout "' . $sBranchname . '" ; ';
-        $sGitCmd .= '     git clone "' . $this->getUrl() . '" "'.$sWorkDir.'" 2>&1 &&   cd  "'.$sWorkDir.'" &&   git checkout "' . $sBranchname . '" 2>&1 ';
-        $this->log(__FUNCTION__." start command <code>$sGitCmd</code>");
+        $sGitCmd .= 'echo git clone "' . $this->getUrl() . '" "' . $sWorkDir . '" 2>&1 \&\& cd  "' . $sWorkDir . '" \&\& git checkout "' . $sBranchname . '" ; ';
+        $sGitCmd .= '     git clone "' . $this->getUrl() . '" "' . $sWorkDir . '" 2>&1 &&   cd  "' . $sWorkDir . '" &&   git checkout "' . $sBranchname . '" 2>&1 ';
+        $this->log(__FUNCTION__ . " start command <code>$sGitCmd</code>");
         // $sReturn = shell_exec($sGitCmd);
         exec($sGitCmd, $aOutput, $iRc);
-        $this->log(__FUNCTION__." command ended with rc=$iRc ". '<pre>'.implode("\n", $aOutput).'</pre>', ($iRc==0 ? 'info':'error'));
-        return implode("\n", $aOutput). "\nrc=$iRc";
+        $this->log(__FUNCTION__ . " command ended with rc=$iRc " . '<pre>' . implode("\n", $aOutput) . '</pre>', ($iRc == 0 ? 'info' : 'error'));
+        return implode("\n", $aOutput) . "\nrc=$iRc";
     }
 
     /**
-     * return url to vcs sources
+     * Get url to vcs sources
+     * 
+     * @return string
      */
-    public function getUrl() {
-        $this->log(__FUNCTION__." --> ".$this->_aCfg["url"]);
-        return $this->_aCfg["url"];
+    public function getUrl(): string
+    {
+        $this->log(__FUNCTION__ . " --> " . $this->_aCfg["url"]);
+        return $this->_aCfg["url"] ?? '';
     }
 
     /**
-     * return url to view sources in webrowser to generate an infolink
+     * Get url to view sources in webrowser to generate an infolink
+     * 
+     * @return string
      */
-    public function getWebGuiUrl() {
-        $this->log(__FUNCTION__." start");
-        return $this->_aCfg["webaccess"];
+    public function getWebGuiUrl(): string
+    {
+        $this->log(__FUNCTION__ . " start");
+        return $this->_aCfg["webaccess"] ?? '';
     }
 
 }
diff --git a/public_html/deployment/classes/vcs.interface.php b/public_html/deployment/classes/vcs.interface.php
index 4ced62baa3d2120643dc6cc981830310cedd30ed..d52a50802460ec613a87da5c0b9177493e9f0aa3 100644
--- a/public_html/deployment/classes/vcs.interface.php
+++ b/public_html/deployment/classes/vcs.interface.php
@@ -4,6 +4,10 @@
  * 
  * interface for a version control system
  * @author hahn
+ * 
+ * Axel: <axel.hahn@unibe.ch>
+ * (...)
+ * 2024-08-28  Axel   php8 only; added variable types; short array syntax
  */
 interface iVcs {
 
@@ -14,46 +18,54 @@ interface iVcs {
     /**
      * return url to vcs sources
      */
-    public function getUrl();
+    public function getUrl(): string;
     
     /**
      * return url to view sources in webrowser to generate an infolink
      */
-    public function getWebGuiUrl();
+    public function getWebGuiUrl(): string;
     
     /**
      * return the build type, i.e. git|svn|cvs|
      */
-    public function getBuildType();
+    public function getBuildType(): string;
     
     // ----------------------------------------------------------------------
     // actions
     // ----------------------------------------------------------------------
+
     /**
      * cleanup unneeded files and directories in a checked out directory
-     * and remove all vcs specific files and directories
+     * and remove all vcs specific files and directories.
+     * This method works on linux only
+     * 
+     * @param string $sWorkDir  path of the build directory to cleanup the vcs meta data from
      * @return bool
      */
-    public function cleanupWorkdir($sWorkDir);
+    public function cleanupWorkdir(string $sWorkDir): bool;
     
     /**
      * get current revision and commit message from remote repository
      * @param boolean  $bRefresh  optional: refresh data; default: use cache
      * @return array
      */
-    public function getRepoRevision($bRefresh=false);
+    public function getRepoRevision(bool $bRefresh=false);
     
     /**
      * get current revision and log message from given directory
-     * @return array
+     * @return bool|array
      */
-    public function getRevision($sWorkDir);
-    
+    public function getRevision(string $sWorkDir = ''): bool|array;
+
     /**
-     * get sources from vsc and check them out in given directory
-     * @return bool
+     * Get sources from vsc and check them out in given directory
+     * It returns false if the workdir was not found or the url for git repo is not set
+     * Otherwise it retunrs the output of the checkout commands
+     * 
+     * @param string $sWorkDir working dir where to check out source url
+     * @return bool|string
      */
-    public function getSources($sWorkDir);
+    public function getSources(string $sWorkDir): bool|string;
     
 
 }
diff --git a/public_html/deployment/inc_functions.php b/public_html/deployment/inc_functions.php
index 57be339e5815a738b33b927fbea28822141955cc..e68f01769be567ed7739b5a43b033b0ca1791765 100644
--- a/public_html/deployment/inc_functions.php
+++ b/public_html/deployment/inc_functions.php
@@ -8,10 +8,12 @@
 
   ---------------------------------------------------------------------
   2013-11-08  Axel <axel.hahn@iml.unibe.ch>
+  ...
+  2024-09-03  Axel <axel.hahn@unibe.ch>  php8 only; added variable types; short array syntax
   ###################################################################### */
 
 global $aParams;
-$aParams = array();
+$aParams = [];
 
 
 // remark: $_SERVER does not exist in CLI
@@ -49,7 +51,7 @@ if (isset($_SERVER) && is_array($_SERVER) && array_key_exists("REQUEST_URI", $_S
             $aParams[$key] = $value;
 
     /* force integer params
-      foreach (array("id") as $sKey) {
+      foreach (["id"] as $sKey) {
       if (array_key_exists($sKey, $aParams)) {
       $aParams[$sKey]=(int)$aParams[$sKey];
       }
@@ -58,34 +60,45 @@ if (isset($_SERVER) && is_array($_SERVER) && array_key_exists("REQUEST_URI", $_S
 
     foreach (array_keys($aParams) as $sKey) {
         $aParams[$sKey] = is_string($aParams[$sKey])
-            ? str_replace(array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $aParams[$sKey])
+            ? str_replace(['\\', "\0", "\n", "\r", "'", '"', "\x1a"], ['\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'], $aParams[$sKey])
             : $aParams[$sKey];
     }
 }
 
 /**
- * get home link as button
+ * Get home link as button
+ * 
+ * @global object $oHtml
+ * 
+ * @param string $sClass css class for the button; default: "btn btn-default"
  * @return string
  */
-function aHome($sClass = "btn btn-default")
+function aHome(string $sClass = "btn btn-default"): string
 {
     global $oHtml;
     // if (!array_key_exists("prj", $aParams)) return false;
-    return $oHtml->getLinkButton(array(
+    return $oHtml->getLinkButton([
         'href' => '/deployment/?',
         'icon' => 'overview',
         'class' => $sClass,
         'label' => t("menu-overview"),
-    ));
+    ]);
 }
 
 /**
- * get project Home link as button
- * @return string
+ * Get project Home link as button.
+ * It returns false if no project is selected (GET param "prj" is missing)
+ * 
+ * @global object $oHtml
+ * @global array $aParams
+ * 
+ * @param string $sClass css class for the button; default: "btn btn-default"
+ * @return bool|string
  */
-function aPrjHome($sClass = "btn btn-default")
+function aPrjHome(string $sClass = "btn btn-default"): bool|string
 {
     global $aParams, $oHtml;
+
     if (!array_key_exists("prj", $aParams)) {
         return false;
     }
@@ -96,51 +109,65 @@ function aPrjHome($sClass = "btn btn-default")
 
     require_once("./classes/project.class.php");
     $oPrj = new project($aParams["prj"]);
-    return $oHtml->getLinkButton(array(
+    return $oHtml->getLinkButton([
         'href' => '/deployment/' . $aParams["prj"] . '/',
         'icon' => 'project',
         'class' => $sClass,
         'label' => $oPrj->getLabel(),
-    ));
+    ]);
 }
 
 /**
- * get go back link as button
+ * Get go back link as button
+ * 
+ * @global object $oHtml
+ * 
+ * @param string $sClass css class for the button; default: "btn btn-default"
  * @return string
  */
-function aGoback($sClass = "btn btn-default")
+function aGoback(string $sClass = "btn btn-default"): string
 {
     global $oHtml;
-    return $oHtml->getLinkButton(array(
+    return $oHtml->getLinkButton([
         'href' => '#',
         'onclick' => 'history.back();',
         'title' => t("back"),
         'icon' => 'back',
         'class' => $sClass,
         'label' => t("back")
-    ));
+    ]);
 }
+
 /**
- * get go top link as button
+ * Get go top link as button
+ * 
+ * @global object $oHtml
+ * 
+ * @param string $sClass css class for the button; default: "scroll-link btn btn-default"
  * @return string
  */
-function aGotop($sClass = "scroll-link btn btn-default")
+function aGotop($sClass = "scroll-link btn btn-default"): string
 {
     global $oHtml;
-    return $oHtml->getLinkButton(array(
+    return $oHtml->getLinkButton([
         'href' => '#top',
         'class' => $sClass,
         'title' => t("gotop"),
         'icon' => 'gotop',
         'label' => ' '
-    ));
+    ]);
 }
 
 /**
- * get array top left navigation
+ * Get array for top left navigation.
+ * It is an empty array if no authenticated user was found.
+ * 
+ * @global object $oHtml
+ * @global array $aParams
+ * 
  * @return array
  */
-function getTopNavLeft($aEmbed = [])
+function getTopNavLeft(): array
 {
     global $aParams, $oHtml;
     $aReturn = [];
@@ -227,10 +254,14 @@ function getTopNavLeft($aEmbed = [])
 }
 
 /**
- * get array top left navigation
+ * Get array for top right navigation.
+ * 
+ * @global object $oHtml
+ * @global array $aParams
+ * 
  * @return array
  */
-function getTopNavRight()
+function getTopNavRight(): array
 {
     global $aParams, $oHtml;
     $aReturn = [];
@@ -273,12 +304,14 @@ function getTopNavRight()
 }
 
 /**
- * get h2 headline with action
- * @global type $aParams
+ * Gett h2 headline with action
+ * 
+ * @global array $aParams
+ * 
  * @param  string  $sLinkClass  classname for links; default: "" (adds class="btn btn-default")
  * @return string
  */
-function getBreadcrumb($sLinkClass = "")
+function getBreadcrumb(string $sLinkClass = ""): string
 {
     global $aParams, $oHtml;
     $sReturn = '';
@@ -309,10 +342,10 @@ function getBreadcrumb($sLinkClass = "")
 }
 
 /**
- * get version info
+ * Get version info by detecting a json file in approot 
  * @return string
  */
-function getVersioninfo()
+function getVersioninfo(): string
 {
     $sMyRev = "";
 
@@ -324,19 +357,22 @@ function getVersioninfo()
     }
     return ($sMyRev  ? $sMyRev . ' @ ' : '') . php_uname("n");
 }
+
 /**
- * translate function
- * @global type $aConfig
+ * translate function. Get translated text by given key.
+ * 
+ * @global array $aConfig
  * @staticvar array $aLang
+ * 
  * @param string $s text
  * @return string
  */
-function t($s)
+function t(string $s): string
 {
     global $aConfig;
-    static $aLang = array();
+    static $aLang = [];
 
-    if (!is_array($aConfig) || !array_key_exists("lang", $aConfig)) {
+    if (!isset($aConfig["lang"])) {
         die("ERROR: \$aConfig[\"lang\"] does not exist.\n");
     }
     if (!count($aLang)) {
@@ -355,11 +391,13 @@ function t($s)
 }
 
 /**
- * enter user and comment 
- * @global type $aParams
- * @return string
+ * Get html code for a form to enter user and comment 
+ * 
+ * @global array $aParams
+ * 
+ * @return string The HTML code
  */
-function enterDeployinfos()
+function enterDeployinfos(): string
 {
     global $aParams;
     $sIdUser = "inputUser";
diff --git a/public_html/deployment/index.php b/public_html/deployment/index.php
index 8bc8d030aefe545edf640bfd915ab675e1dd0fa4..70b8f86e851fc6fc54412b05d3eaf1fc04e719d0 100644
--- a/public_html/deployment/index.php
+++ b/public_html/deployment/index.php
@@ -1,4 +1,15 @@
 <?php
+/* ######################################################################
+
+  IML DEPLOYMENT
+
+  MAIN FILE FOR WEB UI
+
+  ---------------------------------------------------------------------
+  2013-11-nn  Axel <axel.hahn@iml.unibe.ch>
+  ...
+  2024-09-03  Axel <axel.hahn@unibe.ch>  php8 only; added variable types; short array syntax
+  ###################################################################### */
 
 define("APP_VERSION", '2.0');
 
@@ -109,7 +120,7 @@ if($oUser->getUsername()){
             /*
             $sTopRight.=''
                 .'<li >'
-                .$oHtml->getLink(array(
+                .$oHtml->getLink([
                     'href'=>'#',
                     // 'onclick'=>'toggleShellWindow(\''.$CI_plugins->getHtmlOutIdWrapper().'\', this);',
                     'onclick'=>'toggleShellWindow(\''.$CI_plugins->getHtmlOutId().'\', this);',
@@ -117,7 +128,7 @@ if($oUser->getUsername()){
                     'aria-expanded'=>'false',
                     'icon'=> (isset($aPluginConfig['icon']) ? $aPluginConfig['icon'] : ''),
                     'label'=>$sPlugin,
-                ))
+                ])
                 .'</li>'
             ;
             */
@@ -193,7 +204,7 @@ if ($oUser->hasPermission('page_'.$sAction)){
 
     if ($oUser->getUsername()){
         require_once("./classes/actionlog.class.php");
-        $aFilter=array('limit'=>'0, 10');
+        $aFilter=['limit'=>'0, 10'];
         if ($sPrj && $sPrj!="all"){
             $aFilter['project']=$sPrj;
         }
@@ -254,7 +265,7 @@ $aTopnav=getTopNavLeft();
 $aReplace['{{NAVI_TOP}}']=''
 . $renderAdminLTE->addWrapper(
     'nav', ['class'=>'main-header navbar navbar-expand navbar-white navbar-light'],
-    $renderAdminLTE->getTopNavigation($aTopnav,false, array_merge($aNavRight, getTopNavRight()), false)
+    $renderAdminLTE->getTopNavigation($aTopnav,[], array_merge($aNavRight, getTopNavRight()), [])
     // add 2nd navbar if needed
 )
 ;
diff --git a/public_html/deployment/index_v1.php b/public_html/deployment/index_v1.php
deleted file mode 100644
index 0257f34ef7baad382af24302d67a60d7db31ec0d..0000000000000000000000000000000000000000
--- a/public_html/deployment/index_v1.php
+++ /dev/null
@@ -1,191 +0,0 @@
-<?php
-
-/* ######################################################################
-
-  IML DEPLOYMENT
-
-  webgui - index file - controller like
-  ensure that you activated the rewrite rules
-
-  RewriteEngine on
-  RewriteCond %{REQUEST_FILENAME} !-f
-  RewriteCond %{REQUEST_URI} !^/server-status$
-  RewriteRule ^(.*)$ index.php [QSA,L]
-
-  ---------------------------------------------------------------------
-  2013-11-08  Axel <axel.hahn@iml.unibe.ch>
-  ###################################################################### */
-
-session_start();
-
-ini_set('display_errors', 1);
-ini_set('display_startup_errors', 1);
-error_reporting(E_ALL);
-
-require_once("./classes/page.class.php");
-require_once("./classes/plugins_renderer.class.php");
-
-// detect first run
-$bFirstRun=!file_exists("../../config/config_custom.php") || !file_exists("../../config/inc_user2roles.php");
-
-require_once("../../config/inc_projects_config.php");
-require_once("./classes/logger.class.php");
-require_once("./classes/user.class.php");
-global $oCLog;
-$oCLog = new logger();
-$oCLog->enableDebugByIp($aConfig['showdebug']['ip']);
-require_once("./inc_functions.php");
-require_once("./classes/htmlguielements.class.php");
-$oHtml=new htmlguielements();
-
-$sPrj = "";
-$sAction = "overview";
-
-// ----------------------------------------------------------------------
-// check params
-// ----------------------------------------------------------------------
-
-if (array_key_exists("prj", $aParams)) {
-    $sPrj = $aParams["prj"];
-}
-if (array_key_exists("action", $aParams)) {
-    if (file_exists(__DIR__ . '/pages/act_' . $aParams["action"] . ".php")) {
-        $sAction = $aParams["action"];
-    }
-}
-$oCLog->add("parsing params "
-        . '<pre>GET '.print_r($_GET, true).'</pre>'
-        . '<pre>POST '.print_r($_POST, true).'</pre>'
-        . '<pre>aParams: '.print_r($aParams, true).'</pre>'
-        );
-
-if($bFirstRun){
-    $sAction='installer';
-}
-    
-// ----------------------------------------------------------------------
-// html header
-// ----------------------------------------------------------------------
-
-$sHeader = "\n<!-- generated CSS for phases -->\n<style>\n";
-foreach ($aConfig["phases"] as $sPhase => $aData) {
-    $sHeader.=array_key_exists("bgdark", $aData["css"]) ? 'th.' . $sPhase . '{' . $aData["css"]["bgdark"] . '}' : '';
-    $sHeader.=array_key_exists("bglight", $aData["css"]) ? 'td.' . $sPhase . ', div.' . $sPhase . '{' . $aData["css"]["bglight"] . '}' : '';
-    $sHeader.=array_key_exists("bgbutton", $aData["css"]) ? 'a.' . $sPhase . ',a.' . $sPhase . ':hover,button.' . $sPhase . ',button.' . $sPhase . ':hover{' . $aData["css"]["bgbutton"] . '}' : '';
-}
-$sHeader.="</style>\n";
-
-// add shellcmd files
-$sShellOuptut='';
-$sTopRight='';
-
-$CI_plugins=new plugin_renderer(isset($aConfig['plugins']) ? $aConfig['plugins'] : []);
-$CI_plugins->setType('shellcmd');
-
-$aEnabledShellPlugins=$CI_plugins->getEnabledPlugins('shellcmd');
-$sHeader.= count($aEnabledShellPlugins) 
-    ? '' 
-        ."\n<!-- for shellcmd plugins -->\n"
-        .'<script src="/vendor/axelhahn/js/ubd.class.js"></script>'."\n"
-        .'<script src="/vendor/winbox/0.2.82/winbox.min.js"></script>'."\n"
-        .'<link rel="stylesheet" type="text/css" href="/vendor/winbox/0.2.82/winbox.min.css"/>'."\n"
-        ."<!-- shellcmd scripts -->\n"
-    : ''
-    ;
-foreach ($aEnabledShellPlugins as $sPlugin){
-    if ($CI_plugins->testPlugin($sPlugin)){
-        $aPluginConfig=$CI_plugins->getPluginConfig();
-        $sHeader.=$CI_plugins->getHtmlLoadScript('render.js');
-        $sShellOuptut.=$CI_plugins->getHtmlOutwindow();
-        $sTopRight.=''
-            .'<li >'
-            .$oHtml->getLink(array(
-                'href'=>'#',
-                // 'onclick'=>'toggleShellWindow(\''.$CI_plugins->getHtmlOutIdWrapper().'\', this);',
-                'onclick'=>'toggleShellWindow(\''.$CI_plugins->getHtmlOutId().'\', this);',
-                'role'=>'button',
-                'aria-expanded'=>'false',
-                'icon'=> (isset($aPluginConfig['icon']) ? $aPluginConfig['icon'] : ''),
-                'label'=>$sPlugin,
-            ))
-            .'</li>'
-        ;
-    }
-}
-
-// ----------------------------------------------------------------------
-// html body
-// ----------------------------------------------------------------------
-
-$sTopArea=getTopArea(['right'=>$sTopRight]);
-$sBanner=isset($aConfig['banner']) && $aConfig['banner'] ? '<div class="alert alert-info">'.$aConfig['banner'].'</div>' : '';
-$sTopAction=getAction();
-
-// ------ action 
-$oUser=new user();
-if (isset($aConfig["auth"]['forceuser']) && $aConfig["auth"]['forceuser']){
-    $oCLog->add("Found config -> auth -> forceuser: using fake identity [".$aConfig["auth"]['forceuser'].']', "warning");
-    $oUser->setUser($aConfig["auth"]['forceuser']);
-}
-
-if ($oUser->hasPermission('page_'.$sAction)){
-
-    $sActionFile = __DIR__ . '/pages/act_' . $sAction . ".php";
-
-    $oCLog->add("including $sActionFile");
-    ob_start();
-    if (!@include($sActionFile)) {
-        include("./pages/error_404.php");
-    }
-    $sPhpOut = ob_get_contents();
-    ob_end_clean();
-    $oCLog->add("including done $sActionFile");
-
-    $oCLog->add("adding actionlog.class");
-
-    if ($oUser->getUsername()){
-        require_once("./classes/actionlog.class.php");
-        $aFilter=array('limit'=>'0, 10');
-        if ($sPrj && $sPrj!="all"){
-            $aFilter['project']=$sPrj;
-        }
-        $oLog=new Actionlog($sPrj);
-        $sPhpOut.='<div class="logs">' . $oLog->renderLogs($aFilter).'</div>';
-    }
-    $oCLog->add("adding actionlog.class done");
-} else {
-    $sPhpOut=$oUser->showDenied();
-    // return false;
-}
-
-// ----------------------------------------------------------------------
-// render page
-// ----------------------------------------------------------------------
-
-$oCLog->add("Finally: rendering page ...");
-
-$sPhpOut = '
-    <br>
-    ' 
-    . $sTopArea  
-    . $sShellOuptut
-    .'
-    <div id="content">
-        ' . $sBanner . $sTopAction . '
-        ' . $sPhpOut . '
-    </div>
-    <div id="footer">
-        '.t("menu-brand").' &copy; 2013-' . date("Y") . ' <a href="https://git-repo.iml.unibe.ch/iml-open-source/imldeployment/" target="_blank">Institut f&uuml;r Medizinische Lehre; Universit&auml;t Bern</a>
-    </div>
-
-    '
-    .$oCLog->render();
-
-$oPage = new Page();
-$oPage->addResponseHeader("Pragma: no-cache");
-$oPage->setOutputtype('html');
-$oPage->setHeader($sHeader);
-$oPage->addJsOnReady('');
-
-$oPage->setContent($sPhpOut);
-echo $oPage->render();
diff --git a/public_html/deployment/pages/act_rollback.php b/public_html/deployment/pages/__unused__act_rollback.php
similarity index 96%
rename from public_html/deployment/pages/act_rollback.php
rename to public_html/deployment/pages/__unused__act_rollback.php
index e61539d8b1c35d130470cdd5a4a1ed13d9a7f9d0..d776bca8b4e9ddd07d0bf6da8705262f7d2dbb61 100644
--- a/public_html/deployment/pages/act_rollback.php
+++ b/public_html/deployment/pages/__unused__act_rollback.php
@@ -116,4 +116,3 @@ if (array_key_exists("confirm", $aParams)) {
 
 // -- Ausgabe
 echo $sOut;
-?>
diff --git a/public_html/deployment/pages/act_about.php b/public_html/deployment/pages/act_about.php
index 612929da07dff7675163ea4a71edb957188466a8..330c26a0e78feda1aead489143c3e922dcf0fac8 100644
--- a/public_html/deployment/pages/act_about.php
+++ b/public_html/deployment/pages/act_about.php
@@ -8,43 +8,46 @@
 
   ---------------------------------------------------------------------
   2023-12-19  Axel <axel.hahn@unibe.ch>
+  2024-09-03  Axel <axel.hahn@unibe.ch>  php8 only; added variable types; short array syntax
   ###################################################################### */
 
-$oHtml = new htmlguielements($sPrj);
+$oHtml = new htmlguielements();
 
-$BODY=
-$renderAdminLTE->addRow(
-    $renderAdminLTE->addCol(
-        $renderAdminLTE->getCard([
-        'type'=>'dark',
-        'variant'=>'outline',
-        'text'=>'<h3>IML CI server</h3>'
-            .$oHtml->getTable([
-                'body'=>[
-                    [t('page-about-version'), getVersioninfo()],
-                    [t('page-about-author'), 'Institute for Medical Education * University of Bern'],
-                    [t('page-about-license'), 'GNU GPL 3.0'],
-                    [t('page-about-source'), '<a href="https://git-repo.iml.unibe.ch/iml-open-source/imldeployment/">https://git-repo.iml.unibe.ch/iml-open-source/imldeployment/</a>'],
-                    [t('page-about-php'), phpversion()],
-                ]
+$BODY =
+    $renderAdminLTE->addRow(
+        $renderAdminLTE->addCol(
+            $renderAdminLTE->getCard([
+                'type' => 'dark',
+                'variant' => 'outline',
+                'text' => '<h3>IML CI server</h3>'
+                    . $oHtml->getTable([
+                        'body' => [
+                            [t('page-about-version'), getVersioninfo()],
+                            [t('page-about-author'), 'Institute for Medical Education * University of Bern'],
+                            [t('page-about-license'), 'GNU GPL 3.0'],
+                            [t('page-about-source'), '<a href="https://git-repo.iml.unibe.ch/iml-open-source/imldeployment/">https://git-repo.iml.unibe.ch/iml-open-source/imldeployment/</a>'],
+                            [t('page-about-php'), phpversion()],
+                        ]
+                    ]),
             ]),
-        ]), 8
-    )
-    .$renderAdminLTE->addCol(
-        $renderAdminLTE->getCard([
-        'type'=>'dark',
-        'variant'=>'outline',
-        'text'=>'<h3>Components</h3>'
-            .$oHtml->getTable([
-                'body'=>[
-                    ['AdminLTE',            '<a href="https://adminlte.io/">https://adminlte.io/</a>'],
-                    ['jquery 3.6.1',        '<a href="https://jquery.com/">https://jquery.com/</a>'],
-                    ['font-awesome 6.4.0',  '<a href="https://fontawesome.com/">https://fontawesome.com/</a>'],
-                    ['visjs 4.21.0',        '<a href="https://visjs.org">https://visjs.org</a>'],
-                    ['winbox 0.2.82',       '<a href="https://nextapps-de.github.io/winbox/">https://nextapps-de.github.io/winbox/</a>'],
-                ]
+            8
+        )
+        . $renderAdminLTE->addCol(
+            $renderAdminLTE->getCard([
+                'type' => 'dark',
+                'variant' => 'outline',
+                'text' => '<h3>Components</h3>'
+                    . $oHtml->getTable([
+                        'body' => [
+                            ['AdminLTE', '<a href="https://adminlte.io/">https://adminlte.io/</a>'],
+                            ['jquery 3.6.1', '<a href="https://jquery.com/">https://jquery.com/</a>'],
+                            ['font-awesome 6.4.0', '<a href="https://fontawesome.com/">https://fontawesome.com/</a>'],
+                            ['visjs 4.21.0', '<a href="https://visjs.org">https://visjs.org</a>'],
+                            ['winbox 0.2.82', '<a href="https://nextapps-de.github.io/winbox/">https://nextapps-de.github.io/winbox/</a>'],
+                        ]
+                    ]),
             ]),
-        ]), 4
-    )
-);
+            4
+        )
+    );
 
diff --git a/public_html/deployment/pages/act_accept.php b/public_html/deployment/pages/act_accept.php
index cd9c666204cb8b7b3eee7be71ca4a64b68158042..1a0175e39853736c946cbc28648a3246007ea430 100644
--- a/public_html/deployment/pages/act_accept.php
+++ b/public_html/deployment/pages/act_accept.php
@@ -8,6 +8,8 @@
 
   ---------------------------------------------------------------------
   2013-11-08  Axel <axel.hahn@iml.unibe.ch>
+  ...
+  2024-09-03  Axel <axel.hahn@unibe.ch>  php8 only; added variable types; short array syntax
   ###################################################################### */
 
 require_once("./classes/project_gui.class.php");
@@ -21,13 +23,13 @@ if (array_key_exists("par3", $aParams)) {
 
 $sOut = '';
 if (array_key_exists("confirm", $aParams)) {
-    $sOut.=$oPrj->accept($sPhase);
+    $sOut .= $oPrj->accept($sPhase);
 } else {
     if (!$sPhase) {
-        $sOut.=$oHtml->getBox("error", t("error-no-phase"));
+        $sOut .= $oHtml->getBox("error", t("error-no-phase"));
     } else {
         if (!$oPrj->canAcceptPhase($sPhase)) {
-            $sOut.= sprintf(t("page-accept-error-cannot-accept-phase"), $sPhase);
+            $sOut .= sprintf(t("page-accept-error-cannot-accept-phase"), $sPhase);
         } else {
             $aPhaseData = $oPrj->getPhaseInfos($sPhase);
             $aConfigPrj = $oPrj->getConfig();
@@ -37,16 +39,16 @@ if (array_key_exists("confirm", $aParams)) {
             $aPhaseData2 = $oPrj->getPhaseInfos($sNext);
 
             if (
-                    array_key_exists("revision", $aPhaseData2["onhold"]) && $aPhaseData2["onhold"]["revision"] == $aPhaseData["deployed"]["revision"]
+                array_key_exists("revision", $aPhaseData2["onhold"]) && $aPhaseData2["onhold"]["revision"] == $aPhaseData["deployed"]["revision"]
             ) {
-                $sOut.=$oHtml->getBox("warning", sprintf(t("page-accept-warning-version-exists-in-next-queue"), $sNext, $sPhase));
+                $sOut .= $oHtml->getBox("warning", sprintf(t("page-accept-warning-version-exists-in-next-queue"), $sNext, $sPhase));
             }
             if (
-                    array_key_exists("revision", $aPhaseData2["ready2install"]) && $aPhaseData2["ready2install"]["revision"] == $aPhaseData["deployed"]["revision"]
+                array_key_exists("revision", $aPhaseData2["ready2install"]) && $aPhaseData2["ready2install"]["revision"] == $aPhaseData["deployed"]["revision"]
             ) {
-                $sOut.=$oHtml->getBox("warning", sprintf(t("page-accept-warning-version-exists-in-next-repo"), $sNext, $sPhase));
+                $sOut .= $oHtml->getBox("warning", sprintf(t("page-accept-warning-version-exists-in-next-repo"), $sNext, $sPhase));
             }
-            $sOut.='
+            $sOut .= '
                    <table>
                     <thead>
                         <tr>
@@ -79,35 +81,36 @@ if (array_key_exists("confirm", $aParams)) {
             ';
 
             // Eingabe Kommentare zum Deployment
-            $sOut.='
+            $sOut .= '
                 <hr>
                    <p>
                         ' . t("url") . ': <a href="' . $sUrl . '">' . $sUrl . '</a><br>
                         ' . sprintf(t("page-accept-info"), $sPhase, $sPhase, $sNext, $sNext) . '
                    </p>';
-            $sOut.='
+            $sOut .= '
                  <form action="?" method="post" enctype="multipart/form-data">
                     <input type="hidden" name="confirm" value="1">
                     <fieldset>
-                        '.aGoback() 
-                        // .$oPrj->renderLink('accept', $sPhase)
-                        .'<button type="submit" class="btn btn-large '.$sNext.'" title="'.sprintf(t("accept-hint"), $sPhase, $sNext).'"'
-                            .'>' . $oHtml->getIcon('accept') . sprintf(t("accept"), $sPhase, $sNext) . '</button>'
-                        .'
+                        ' . aGoback()
+                // .$oPrj->renderLink('accept', $sPhase)
+                . '<button type="submit" class="btn btn-large ' . $sNext . '" title="' . sprintf(t("accept-hint"), $sPhase, $sNext) . '"'
+                . '>' . $oHtml->getIcon('accept') . sprintf(t("accept"), $sPhase, $sNext) . '</button>'
+                . '
                     </fieldset>
                  </form>
                  ';
-            $BODY=
-            $renderAdminLTE->addRow(
-                $renderAdminLTE->addCol(
-                    $renderAdminLTE->getCard([
-                    'type'=>'success',
-                    'variant'=>'outline',
-                    'text'=>$sOut,
-                    ]), 12
-                )
-            );
-         
+            $BODY =
+                $renderAdminLTE->addRow(
+                    $renderAdminLTE->addCol(
+                        $renderAdminLTE->getCard([
+                            'type' => 'success',
+                            'variant' => 'outline',
+                            'text' => $sOut,
+                        ]),
+                        12
+                    )
+                );
+
         }
     }
 }
@@ -116,4 +119,3 @@ if (array_key_exists("confirm", $aParams)) {
 
 // -- Ausgabe
 echo $sOut;
-?>
diff --git a/public_html/deployment/pages/act_build.php b/public_html/deployment/pages/act_build.php
index 6d03699eda0e3cd0ae971fb54efb620091412ca6..00c9cc94972df2e820f9f4d19ef6f95fd8ee5ede 100644
--- a/public_html/deployment/pages/act_build.php
+++ b/public_html/deployment/pages/act_build.php
@@ -27,7 +27,7 @@ if (array_key_exists("branchname", $aParams)) {
     $sBranchname = $aParams["branchname"];
     $oPrj->setBranchname($aParams["branchname"]);
 } else {
-    $sBranchname = $oPrj->getBranchname(true);
+    $sBranchname = $oPrj->getBranchname();
 }
 
 if (!array_key_exists("confirm", $aParams)) {
@@ -36,60 +36,60 @@ if (!array_key_exists("confirm", $aParams)) {
     // ------------------------------------------------------------
     $sNext = $oPrj->getNextPhase();
     $aPhaseData2 = $oPrj->getPhaseInfos($sNext);
-    $sBranchontarget=isset($aPhaseData2["ready2install"]["branch"]) ? $aPhaseData2["ready2install"]["branch"]: '';
+    $sBranchontarget = isset($aPhaseData2["ready2install"]["branch"]) ? $aPhaseData2["ready2install"]["branch"] : '';
     print_r($sBranchname);
     $bIgnoreCache = isset($aParams['reloadBranches']) ? $aParams['reloadBranches'] : false;
 
     // $sOut.='<p>Re-Read Branches (ignore caching) - '.($bIgnoreCache ? "JA" : "mein" ).'</p>';
 
-    $sOut.='<p>' . sprintf(t("page-build-info"), $sNext, $sNext) . '</p>';
+    $sOut .= '<p>' . sprintf(t("page-build-info"), $sNext, $sNext) . '</p>';
 
     $sRevison = false;
     $aRepodata = $oPrj->getRepoRevision(true);
     if (isset($aRepodata["revision"])) {
         $sRevison = $aRepodata["revision"];
         if (
-                array_key_exists("revision", $aPhaseData2["onhold"]) && $aPhaseData2["onhold"]["revision"] == $sRevison
+            array_key_exists("revision", $aPhaseData2["onhold"]) && $aPhaseData2["onhold"]["revision"] == $sRevison
         ) {
-            $sOut.=$oHtml->getBox("warning", "In der Queue von [$sNext] ist die Version bereits $sRevison vorhanden!");
+            $sOut .= $oHtml->getBox("warning", sprintf(t("page-build-warning-version-exists"), $sNext, $sRevison));
         }
         if (
-                array_key_exists("revision", $aPhaseData2["ready2install"]) && $aPhaseData2["ready2install"]["revision"] == $sRevison
+            array_key_exists("revision", $aPhaseData2["ready2install"]) && $aPhaseData2["ready2install"]["revision"] == $sRevison
         ) {
-            $sOut.=$oHtml->getBox("warning", "Im Repo von [$sNext] ist die Version $sRevison bereits vorhanden!");
+            $sOut .= $oHtml->getBox("warning", sprintf(t("page-build-warning-version-is-installed"), $sNext, $sRevison));
         }
     }
-    $sOut.='
+    $sOut .= '
         <table>
              <thead>
                  <tr>
-                     <th class="versioncontrol" colspan="3">' . $oHtml->getIcon('repository').t("versioncontrol") . '<br></th>
+                     <th class="versioncontrol" colspan="3">' . $oHtml->getIcon('repository') . t("versioncontrol") . '<br></th>
                      <th> </th>
-                     <th class="' . $sNext . '" colspan="2">' . $oHtml->getIcon('phase').$sNext . '</th>
+                     <th class="' . $sNext . '" colspan="2">' . $oHtml->getIcon('phase') . $sNext . '</th>
                  </tr>
              </thead>
              <tbody>
                  <tr>
                      <td class="">
                          ' . $oPrj->renderSelectRemoteBranches(false, $bIgnoreCache) . '
-                         '.(
-                            $sBranchontarget && $sBranchontarget != $sBranchname
-                             ? '
+                         ' . (
+        $sBranchontarget && $sBranchontarget != $sBranchname
+        ? '
                              <form action="?" method="post" enctype="multipart/form-data">
                                  <input type="hidden" name="reloadBranches" value="1">
                                  <input type="hidden" name="branchname" value="' . $sBranchontarget . '">
-                                    '.sprintf(t('page-build-branch-on-target'), $sBranchontarget).'<br>
+                                    ' . sprintf(t('page-build-branch-on-target'), $sBranchontarget) . '<br>
                                      <button type="submit" class="btn btn-default">' . sprintf(t('page-build-switch-to-target'), $sBranchontarget) . '</button>
                              </form>
                              <br><hr>
                              '
-                             : ''
-                         ).'
+        : ''
+    ) . '
                           <form action="?" method="post" enctype="multipart/form-data">
                             <input type="hidden" name="reloadBranches" value="1">
                             <input type="hidden" name="branchname" value="' . $sBranchname . '">
                             <fieldset>
-                                <button type="submit" class="btn btn-default">' . $oHtml->getIcon('refresh').t("page-build-reload-branches") . '</button>
+                                <button type="submit" class="btn btn-default">' . $oHtml->getIcon('refresh') . t("page-build-reload-branches") . '</button>
                             </fieldset>
                         </form>
      
@@ -117,7 +117,7 @@ if (!array_key_exists("confirm", $aParams)) {
 
     // Eingabe Kommentare zum Deployment
     if ($sRevison) {
-        $sOut.='
+        $sOut .= '
                  <form action="?" method="post" enctype="multipart/form-data">
                     <input type="hidden" name="confirm" value="1">
                     <input type="hidden" name="branchname" value="' . $sBranchname . '">
@@ -125,20 +125,21 @@ if (!array_key_exists("confirm", $aParams)) {
                      <hr>
                     -->
                     <fieldset>
-                        '.aGoback().'
-                        <button type="submit" class="btn btn-large '.$sNext.'" >' . $oHtml->getIcon('build').sprintf(t("page-build-buttonlabel"), $sNext) . '</button>
+                        ' . aGoback() . '
+                        <button type="submit" class="btn btn-large ' . $sNext . '" >' . $oHtml->getIcon('build') . sprintf(t("page-build-buttonlabel"), $sNext) . '</button>
                     </fieldset>
                  </form>
                  ';
     }
-    $BODY=
+    $BODY =
         $renderAdminLTE->addRow(
             $renderAdminLTE->addCol(
                 $renderAdminLTE->getCard([
-                'type'=>'success',
-                'variant'=>'outline',
-                'text'=>$sOut,
-                ]), 12
+                    'type' => 'success',
+                    'variant' => 'outline',
+                    'text' => $sOut,
+                ]),
+                12
             )
         );
 
@@ -186,17 +187,17 @@ if (!array_key_exists("confirm", $aParams)) {
     $sAjaxFile = $aParams["prj"] . "_" . $aParams["action"];
     $sDivname = "outAjax";
     $sUrlStartAction = "/deployment/?"
-            . "&prj=" . $aParams["prj"]
-            . "&action=" . $aParams["action"]
-            . "&confirm=" . $aParams["confirm"]
-            . "&branchname=" . $aParams["branchname"]
-            . "&ajax=" . $sAjaxFile
-            . "&run=1"
+        . "&prj=" . $aParams["prj"]
+        . "&action=" . $aParams["action"]
+        . "&confirm=" . $aParams["confirm"]
+        . "&branchname=" . $aParams["branchname"]
+        . "&ajax=" . $sAjaxFile
+        . "&run=1"
     ;
-    $sUrlFile = "/webservice/getfile.php?". "&ajax=" . $sAjaxFile;
-    
-    $sOut.= '<div id="' . $sDivname . '"></div>'
-            . '<script>
+    $sUrlFile = "/webservice/getfile.php?" . "&ajax=" . $sAjaxFile;
+
+    $sOut .= '<div id="' . $sDivname . '"></div>'
+        . '<script>
                     var iRepeat=2000;
                     
                     // start build process
diff --git a/public_html/deployment/pages/act_checkssh.php b/public_html/deployment/pages/act_checkssh.php
index 49c0aa76ff369177445135403d99738d720aa340..c7048af370e5b7d2dab11ee4a0045030957cbe4b 100644
--- a/public_html/deployment/pages/act_checkssh.php
+++ b/public_html/deployment/pages/act_checkssh.php
@@ -8,18 +8,17 @@
  
   ---------------------------------------------------------------------
   2018-02-01  Axel <axel.hahn@iml.unibe.ch>
+  ...
+  2024-09-03  Axel <axel.hahn@unibe.ch>  php8 only; added variable types; short array syntax
   ###################################################################### */
 
-$sOK='<span class="ok">' . t('ok') . '</span>';
-$sFAILED='<span class="error">' . t('error') . '</span>';
-$aErrors=array();
+$sOK = '<span class="ok">' . t('ok') . '</span>';
+$sFAILED = '<span class="error">' . t('error') . '</span>';
+$aErrors = [];
 global $sOK;
 global $sFAILED;
 global $aErrors;
 
-// test only
-// $aConfig['mirrorPackages']=array('puppet' => array('target' => 'ladmin@calcium.iml.unibe.ch:/share/imldeployment'),);
-
 // ----------------------------------------------------------------------
 // functions
 // ----------------------------------------------------------------------
@@ -29,19 +28,21 @@ global $aErrors;
  * 
  * @global string $sOK
  * @global string $sFAILED
+ * 
  * @param string   $sCmd  command to execute
  * @return string
  */
-function myExec($sCmd){
+function myExec(string $sCmd): string
+{
     global $sOK, $sFAILED, $aErrors;
-    $sReturn='';
-    
+    $sReturn = '';
+
     exec($sCmd . " 2>&1 && echo '$sOK' || echo '$sFAILED' ", $aOut);
     $sReturn .= "<pre><strong>\$ $sCmd</strong><br>"
-            . implode('<br>', $aOut)."<br>"
-            . "</pre>";
-    if (strpos($sReturn, $sFAILED)){
-        $aErrors[]=$sCmd;
+        . implode('<br>', $aOut) . "<br>"
+        . "</pre>";
+    if (strpos($sReturn, $sFAILED)) {
+        $aErrors[] = $sCmd;
     }
     return $sReturn;
 }
@@ -52,15 +53,15 @@ function myExec($sCmd){
 
 $sOut = '<h2>SSH</h2>';
 
-if(isset($aConfig['mirrorPackages']) && count($aConfig['mirrorPackages'])){
+if (isset($aConfig['mirrorPackages']) && count($aConfig['mirrorPackages'])) {
     // test puppet
     $sOut = '<h3>Puppet</h3>'
-            . '<ul>';
-    foreach ($aConfig['mirrorPackages'] as $sServer=>$aSettings) {
-        $sCmd = 'ssh ' . preg_replace('#\:\/.*#', '' , $aSettings['target']) . ' echo "hello from \`hostname -f\`"';
+        . '<ul>';
+    foreach ($aConfig['mirrorPackages'] as $sServer => $aSettings) {
+        $sCmd = 'ssh ' . preg_replace('#\:\/.*#', '', $aSettings['target']) . ' echo "hello from \`hostname -f\`"';
         $sOut .= '<li><strong>' . $sServer . '</strong> ' . myExec($sCmd);
     }
-    $sOut.='</ul>';
+    $sOut .= '</ul>';
 }
 
 // ... then loop over all projects ...
@@ -69,36 +70,36 @@ foreach ($oPrj1->getProjects() as $sPrj) {
     $oPrj = new project($sPrj);
     $aPrjConfig = $oPrj->getConfig();
     $sOut .= '<h3>' . $oPrj->getLabel() . '</h3>'
-            // . '<pre>'.print_r($aPrjConfig, 1).'</pre>'
-            . '<ul>';
+        // . '<pre>'.print_r($aPrjConfig, 1).'</pre>'
+        . '<ul>';
 
     // test repository
-    $sOut .= '<li>'.t('repositoryinfos').' <strong>' . $aPrjConfig['build']['url'] . '</strong> - ';
-    if($oPrj->getRepoRevision()){
+    $sOut .= '<li>' . t('repositoryinfos') . ' <strong>' . $aPrjConfig['build']['url'] . '</strong> - ';
+    if ($oPrj->getRepoRevision()) {
         $sOut .= $sOK;
     } else {
         $sOut .= $sFAILED;
         $aErrors[] = $aPrjConfig['build']['url'];
     }
-    
+
     // loop over phases ...
     foreach (array_keys($oPrj->getPhases()) as $sPhase) {
-        $sOut .= '<li>'.t('phase').' <strong>' . $sPhase . '</strong><ul>';
+        $sOut .= '<li>' . t('phase') . ' <strong>' . $sPhase . '</strong><ul>';
         $sDeployhosts = array_key_exists("hosts", $aPrjConfig["phases"][$sPhase]) ? $aPrjConfig["phases"][$sPhase]["hosts"] : "";
 
         if ($sDeployhosts) {
-            foreach(explode(',', $sDeployhosts) as $sSingleSshTarget){
+            foreach (explode(',', $sDeployhosts) as $sSingleSshTarget) {
                 $sOut .= '<li>' . $sSingleSshTarget . '<br>';
                 $sCmd = 'ssh ' . $aConfig["installPackages"]["user"] . '@' . $sSingleSshTarget . ' echo "hello from \`hostname -f\`"';
-                $sOut.= myExec($sCmd);
+                $sOut .= myExec($sCmd);
             }
         }
-        $sOut.='</ul>';
+        $sOut .= '</ul>';
     }
-    $sOut.='</ul>';
+    $sOut .= '</ul>';
 }
 
-if(count($aErrors)){
-    echo $oHtml->getBox('error', '<ol><li>'.implode('<li>', $aErrors), '</ol>');
+if (count($aErrors)) {
+    echo $oHtml->getBox('error', '<ol><li>' . implode('<li>', $aErrors). '</ol>');
 }
 echo $sOut;
diff --git a/public_html/deployment/pages/act_cleanup.php b/public_html/deployment/pages/act_cleanup.php
index 33d53bffbfbc0b5ee8cae6ca7c26d79dd8d9ff94..9acf3782af5cc34f15bb379ea324480c2de0e518 100644
--- a/public_html/deployment/pages/act_cleanup.php
+++ b/public_html/deployment/pages/act_cleanup.php
@@ -36,4 +36,3 @@ $sOut = '
 
 // -- Ausgabe
 echo $sOut;
-?>
diff --git a/public_html/deployment/pages/act_delete.php b/public_html/deployment/pages/act_delete.php
index cbd4d97ca3b922d484a1adec5851bc031745f5f2..b2d9ada36e24cd001216c4dfb33b777cba8a4dde 100644
--- a/public_html/deployment/pages/act_delete.php
+++ b/public_html/deployment/pages/act_delete.php
@@ -11,6 +11,8 @@
 
   ---------------------------------------------------------------------
   2014-03-24  Axel <axel.hahn@iml.unibe.ch>
+  ...
+  2024-09-03  Axel <axel.hahn@unibe.ch>  php8 only; added variable types; short array syntax
   ###################################################################### */
 
 require_once("./classes/project_gui.class.php");
@@ -26,80 +28,80 @@ if (!array_key_exists("confirm", $aParams)) {
     // ----- form to confirm deletion
 
     $sOut .= '<p>' . t('page-delete-project-introtext') . '</p>'
-            . $oPrj->renderVersionUsage() . '<br><br>';
+        . $oPrj->renderVersionUsage() . '<br><br>';
 
 
     $i = 0;
-    $aForms = array(
-        'setup' => array(
-            'meta' => array(
+    $aForms = [
+        'setup' => [
+            'meta' => [
                 'method' => 'POST',
                 'action' => '?',
-            ),
-            'validate' => array(),
-            'form' => array(
-                'input' . $i++ => array(
+            ],
+            'validate' => [],
+            'form' => [
+                'input' . $i++ => [
                     'type' => 'hidden',
                     'name' => 'confirm',
                     'value' => '1',
-                ),
-                'input' . $i++ => array(
+                ],
+                'input' . $i++ => [
                     'type' => 'checkbox',
                     'name' => 'removeOptions',
                     'label' => t("page-delete-remove-options"),
                     'validate' => 'isastring',
                     'value' => 1,
-                    'options' => array(
-                        'bRemoveRepolinks' => array(
+                    'options' => [
+                        'bRemoveRepolinks' => [
                             'label' => t("page-delete-cb-label-remove-links-for-repo"),
                             'checked' => false,
-                        ),
-                        'bRemoveArchive' => array(
+                        ],
+                        'bRemoveArchive' => [
                             'label' => t("page-delete-cb-label-remove-archive"),
                             'checked' => false,
-                        ),
-                        'bRemoveConfig' => array(
+                        ],
+                        'bRemoveConfig' => [
                             'label' => t("page-delete-cb-label-remove-config"),
                             'checked' => true,
-                        ),
-                    ),
-                ),
-                'markup' . $i++ => array(
+                        ],
+                    ],
+                ],
+                'markup' . $i++ => [
                     'type' => 'markup',
                     'value' => '<hr>',
-                ),
-                'button' . $i++ => array(
+                ],
+                'button' . $i++ => [
                     'type' => 'submit',
                     'class' => 'btn-danger',
                     'value' => $oHtml->getIcon('delete') . t("page-delete-project-buttonlabel"),
-                ),
-            ),
-        ),
-    );
+                ],
+            ],
+        ],
+    ];
     $oForm = new formgen($aForms);
     $sOut .= $oForm->renderHtml("setup");
-    $sHome= aPrjHome();
-    
+    $sHome = aPrjHome();
+
 } else {
 
     // ----- delete the project
-    $sHome= aHome();
+    $sHome = aHome();
 
-    $aOptions = array();
-    foreach (array("bRemoveRepolinks", "bRemoveArchive", "bRemoveConfig") as $sOption) {
+    $aOptions = [];
+    foreach (["bRemoveRepolinks", "bRemoveArchive", "bRemoveConfig"] as $sOption) {
         if (array_search($sOption, $aParams["removeOptions"]) !== false) {
             $aOptions[$sOption] = 1;
         }
     }
-    $sErrors=$oPrj->delete($aOptions);
+    $sErrors = $oPrj->delete($aOptions);
     if ($sErrors) {
-        $sOut.=$oHtml->getBox("error", t('page-delete-project-delete-failed') . $sErrors);
+        $sOut .= $oHtml->getBox("error", t('page-delete-project-delete-failed') . $sErrors);
     } else {
-        $sOut.=$oHtml->getBox("success", t('page-delete-project-delete-success'));
+        $sOut .= $oHtml->getBox("success", t('page-delete-project-delete-success'));
     }
 }
 
 // $sOut.= '<div id="navbuttom">' . $sHome . '</div>';
 
-    // -- Ausgabe
+// -- Ausgabe
 echo $sOut;
diff --git a/public_html/deployment/pages/act_deploy.php b/public_html/deployment/pages/act_deploy.php
index 6d0b5df987273d439330e6d61427509d389c2eb4..60044b4b39d21611316bc8f75b065571b7741901 100644
--- a/public_html/deployment/pages/act_deploy.php
+++ b/public_html/deployment/pages/act_deploy.php
@@ -8,6 +8,8 @@
 
   ---------------------------------------------------------------------
   2013-11-08  Axel <axel.hahn@iml.unibe.ch>
+  ...
+  2024-09-03  Axel <axel.hahn@unibe.ch>  php8 only; added variable types; short array syntax
   ###################################################################### */
 
 require_once("./classes/project_gui.class.php");
@@ -50,39 +52,39 @@ if (array_key_exists("confirm", $aParams)) {
 
             // Eingabe Kommentare zum Deployment
             $i = 0;
-            $aForms = array(
-                'deploy' => array(
-                    'meta' => array(
+            $aForms = [
+                'deploy' => [
+                    'meta' => [
                         'method' => 'POST',
                         'action' => '?',
-                    ),
-                    'validate' => array(),
-                    'form' => array(
-                        'input' . $i++ => array(
+                    ],
+                    'validate' => [],
+                    'form' => [
+                        'input' . $i++ => [
                             'type' => 'hidden',
                             'name' => 'confirm',
                             'value' => '1',
-                        ),
-                        'input' . $i++ => array(
+                        ],
+                        'input' . $i++ => [
                             'type' => 'checkbox',
                             'name' => 'aIgnore',
                             'label' => t("deploy-settings"),
                             'validate' => 'isastring',
-                            'options' => array(
-                                'bIgnoreDeploytimes' => array(
+                            'options' => [
+                                'bIgnoreDeploytimes' => [
                                     'label' => t("page-deploy-info-ignore-deploytime"),
                                     'checked' => false,
-                                ),
-                            ),
-                        ),
-                        'button' . $i++ => array(
+                                ],
+                            ],
+                        ],
+                        'button' . $i++ => [
                             'type' => 'submit',
                             'class' => $sPhase,
                             'value' => $oHtml->getIcon('deploy').t("deploy"),
-                        ),
-                    ),
-                ),
-            );
+                        ],
+                    ],
+                ],
+            ];
             $oForm = new formgen($aForms);
             $sOut .= $oForm->renderHtml("deploy");
 
@@ -107,4 +109,3 @@ if (array_key_exists("confirm", $aParams)) {
 
 // -- Ausgabe
 echo $sOut;
-?>
diff --git a/public_html/deployment/pages/act_doc.php b/public_html/deployment/pages/act_doc.php
index 806a54da58e8f17bd31e19ded9bd27429d4ff32f..79faa328613008d1f8de43ac3d96318d4c29f252 100644
--- a/public_html/deployment/pages/act_doc.php
+++ b/public_html/deployment/pages/act_doc.php
@@ -8,57 +8,56 @@
 
   ---------------------------------------------------------------------
   2013-11-19  Axel <axel.hahn@iml.unibe.ch>
+  ...
+  2024-09-03  Axel <axel.hahn@unibe.ch>  php8 only; added variable types; short array syntax
   ###################################################################### */
 
 require_once("./classes/classinfos.class.php");
-$aClasses = array(
-    "actionlog" => array("name" => "actionLog"),
-    "cache" => array("name" => "AhCache"),
-    "config-replacement" => array("name" => "configreplacement"),
-    "deploy-foreman" => array("name" => "deployForeman"),
-    "foremanapi" => array("name" => "ForemanApi"),
-    "formgen" => array("name" => "formgen"),
-    "htmlguielements" => array("name" => "htmlguielements"),
-    "ldap" => array("name" => "imlldap"),
-    "logger" => array("name" => "logger"),
-    "messenger" => array("name" => "messenger"),
-    "page" => array("name" => "Page"),
-    "project" => array("name" => "project"),
-    "projectlist" => array("name" => "projectlist"),
-    "rollout_base" => array("name" => "rollout_base"),
-    "user" => array("name" => "user"),
-    "vcs.git" => array("name" => "vcs"),
-
-);
+$aClasses = [
+    "actionlog" => ["name" => "actionLog"],
+    // "cache" => ["name" => "AhCache"),
+    "config-replacement" => ["name" => "configreplacement"],
+    "foremanapi" => ["name" => "ForemanApi"],
+    "formgen" => ["name" => "formgen"],
+    "htmlguielements" => ["name" => "htmlguielements"],
+    "ldap" => ["name" => "imlldap"],
+    "logger" => ["name" => "logger"],
+    "messenger" => ["name" => "messenger"],
+    "page" => ["name" => "Page"],
+    "project" => ["name" => "project"],
+    "projectlist" => ["name" => "projectlist"],
+    "rollout_base" => ["name" => "rollout_base"],
+    "user" => ["name" => "user"],
+    "vcs.git" => ["name" => "vcs"],
+
+];
 
 $sOut = '';
-$sOut.='<a href="/deployment/all/doc/">Start</a> | ';
+$sOut .= '<a href="/deployment/all/doc/">Start</a> | ';
 foreach ($aClasses as $sClassfile => $aInfos) {
-    $sOut.='<a href="/deployment/all/doc/' . $sClassfile . '/">' . $sClassfile . '</a> | ';
+    $sOut .= '<a href="/deployment/all/doc/' . $sClassfile . '/">' . $sClassfile . '</a> | ';
 }
-$sOut.= '<hr>';
+$sOut .= '<hr>';
 
 if (array_key_exists("par3", $aParams)) {
     $sClass = $aParams["par3"];
     if (!array_key_exists($sClass, $aClasses)) {
-        $sOut.= $oHtml->getBox("error", sprintf(t("page-doc-error-class-not-configured"), $sClass, __FILE__));
+        $sOut .= $oHtml->getBox("error", sprintf(t("page-doc-error-class-not-configured"), $sClass, __FILE__));
     } else {
         require_once("./classes/$sClass.class.php");
         $o = new classinfos($aClasses[$sClass]["name"]);
-        $sOut.=t("page-doc-info-" . $sClass) . $o->render();
+        $sOut .= t("page-doc-info-" . $sClass) . $o->render();
     }
 } else {
-    $sOut.=t("page-doc-info-select-class") . '<ul>';
+    $sOut .= t("page-doc-info-select-class") . '<ul>';
     foreach (array_keys($aClasses) as $sClassfile) {
-        $sOut.='<li>'
-                . '<a href="/deployment/all/doc/' . $sClassfile . '/">' . $sClassfile . '</a>'
-                . ' ' . t("page-doc-info-" . $sClassfile)
-                . '</li>';
+        $sOut .= '<li>'
+            . '<a href="/deployment/all/doc/' . $sClassfile . '/">' . $sClassfile . '</a>'
+            . ' ' . t("page-doc-info-" . $sClassfile)
+            . '</li>';
     }
-    $sOut.='</ul>';
+    $sOut .= '</ul>';
 }
 
 // -- Ausgabe
 echo $sOut;
-?>
-
diff --git a/public_html/deployment/pages/act_htmltest.php b/public_html/deployment/pages/act_htmltest.php
index 4669c917bd1fb1df8bcf633a891fffa25529ce0a..31e7f5833b3659257c88044e993e984874de4564 100644
--- a/public_html/deployment/pages/act_htmltest.php
+++ b/public_html/deployment/pages/act_htmltest.php
@@ -4,139 +4,143 @@
 
   IML DEPLOYMENT
 
-  webgui - build a package
+  webgui - html tester
 
   ---------------------------------------------------------------------
-  2014-11-14  Axel <axel.hahn@iml.unibe.ch>  selector for branches
-  2014-02-14  Axel <axel.hahn@iml.unibe.ch>  build was "ajaxified"
-  2013-11-08  Axel <axel.hahn@iml.unibe.ch>
+  ...
+  2024-09-03  Axel <axel.hahn@unibe.ch>  php8 only; added variable types; short array syntax
   ###################################################################### */
 
 require_once("./classes/project.class.php");
 
 
 /**
- * helper: render html code for a table row
+ * helper: get html code for a table row
  * @global htmlguielements $oHtml
  * @param string   $sDescr  description
  * @param string   $sCode   php code
  * @return string
  */
-function addHtmltestTest($sDescr, $sCode){
-    $oHtml=new htmlguielements();
-    $sOut='??';
-    eval("\$sOut=$sCode;");
-    
+function addHtmltestTest(string $sDescr, string $sCode): string
+{
+    $oHtml = new htmlguielements();
+    $sOut = '??';
+    eval ("\$sOut=$sCode;");
+
     return '<tr>'
-    . '<td>'
-            .  $sDescr
-    . '</td>'
-    . '<td style="padding: 0 1em;"><pre>'
-            . htmlentities($sCode)
-    . '</pre></td>'
-    . '<td style="padding: 0 1em;">'
-            .$sOut
-    . '</td>'
-    . '<td style="padding: 0 1em;"><pre>'
-            .str_replace('&gt;', '&gt;<br>',htmlentities($sOut))
-    . '</pre></td>'
-    . '</tr>';
+        . '<td>'
+        . $sDescr
+        . '</td>'
+        . '<td style="padding: 0 1em;"><pre>'
+        . htmlentities($sCode)
+        . '</pre></td>'
+        . '<td style="padding: 0 1em;">'
+        . $sOut
+        . '</td>'
+        . '<td style="padding: 0 1em;"><pre>'
+        . str_replace('&gt;', '&gt;<br>', htmlentities($sOut))
+        . '</pre></td>'
+        . '</tr>';
 }
 
 
 // ----------------------------------------------------------------------
 // MAIN
 // ----------------------------------------------------------------------
-$oHtml=new htmlguielements();
+$oHtml = new htmlguielements();
 
 // generate a List of al icons
-$sIconlist='';
-$sIconlist.='<strong>Buttons</strong><br>';
-foreach ($oHtml->aCfg['buttons'] as $sLabel=>$aItems){
-    $sIconlist.=$oHtml->getIcon($aItems['icon']).' - '.$sLabel.' - '.$aItems['icon'].'<br>';
+$sIconlist = '';
+$sIconlist .= '<strong>Buttons</strong><br>';
+
+        
+
+foreach ($oHtml->aCfg['buttons'] as $sLabel => $aItems) {
+    $sIconlist .= $oHtml->getIcon((string)$aItems['icon']) . ' - ' . $sLabel . ' - ' . $aItems['icon'] . '<br>';
 }
-$sIconlist.='<br><strong>Icons</strong><br>';
-foreach ($oHtml->aCfg['icons'] as $sLabel=>$sIcon){
-    $sIconlist.=$oHtml->getIcon($sIcon).' - '.$sLabel.': '.$sIcon.'<br>';
+$sIconlist .= '<br><strong>Icons</strong><br>';
+foreach ($oHtml->aCfg['icons'] as $sLabel => $sIcon) {
+    $sIconlist .= $oHtml->getIcon($sIcon) . ' - ' . $sLabel . ': ' . $sIcon . '<br>';
 }
 
-$sRows=''        
-   .  addHtmltestTest("Box zeichnen - Fehler", "\$oHtml->getBox('error', 'errormessage')")
-   .  addHtmltestTest("Box zeichnen - Warnung", "\$oHtml->getBox('warning', 'Message')")
-   .  addHtmltestTest("Box zeichnen - Info", "\$oHtml->getBox('info', 'Message')")
-   .  addHtmltestTest("Box zeichnen - OK", "\$oHtml->getBox('success', 'Message')")
-        
-   .  addHtmltestTest("Icon - Fontawesome", "\$oHtml->getIcon('fa-close');")
-   .  addHtmltestTest("Icon - Glyphicon", "\$oHtml->getIcon('glyphicon-user');")
-        
-   .  '<tr>'
-        . '<td>pre-defined icons</td>'
-        . '<td>-</td>'
-        . '<td>'.$sIconlist.'</td>'
-        . '<td>-</td>'
-   . '</tr>' 
-
-   .  addHtmltestTest("Link", "\$oHtml->getLink(array(
+$sRows = ''
+    . addHtmltestTest("Box zeichnen - Fehler", "\$oHtml->getBox('error', 'errormessage')")
+    . addHtmltestTest("Box zeichnen - Warnung", "\$oHtml->getBox('warning', 'Message')")
+    . addHtmltestTest("Box zeichnen - Info", "\$oHtml->getBox('info', 'Message')")
+    . addHtmltestTest("Box zeichnen - OK", "\$oHtml->getBox('success', 'Message')")
+
+    . addHtmltestTest("Icon - Fontawesome", "\$oHtml->getIcon('fa-close');")
+
+    . '<tr>'
+    . '<td>pre-defined icons</td>'
+    . '<td>-</td>'
+    . '<td>' . $sIconlist . '</td>'
+    . '<td>-</td>'
+    . '</tr>'
+
+    . addHtmltestTest("Link", "\$oHtml->getLink([
     'href'=>'https://www.axel-hahn.de/',
     'icon'=>'fa-globe',
     'label'=>'Axels Webseite',
-));")
-   .  addHtmltestTest("Link als Button", "\$oHtml->getLinkButton(array(
+]);")
+    . addHtmltestTest("Link als Button", "\$oHtml->getLinkButton([
     'href'=>'https://www.axel-hahn.de/',
     'target'=>'_blank',
     'icon'=>'fa-globe',
     'label'=>'Axels Webseite',
-));")
-   .  addHtmltestTest("Link als Button mit Type OK", "\$oHtml->getLinkButton(array('type'=>'ok',));")
-   .  addHtmltestTest("Link als Button mit Type close", "\$oHtml->getLinkButton(array('type'=>'close',));")
-   .  addHtmltestTest("Link als Button mit Type error", "\$oHtml->getLinkButton(array('type'=>'error','label'=>'Fehler'));")
+]);")
+    . addHtmltestTest("Link als Button mit Type OK", "\$oHtml->getLinkButton(['type'=>'ok',]);")
+    . addHtmltestTest("Link als Button mit Type close", "\$oHtml->getLinkButton(['type'=>'close',]);")
+    . addHtmltestTest("Link als Button mit Type error", "\$oHtml->getLinkButton(['type'=>'error','label'=>'Fehler']);")
 
-//    . addHtmltestTest("Tabs", "\$oHtml->getNav(
-//     array(
-//         'options' => array(
+    //    . addHtmltestTest("Tabs", "\$oHtml->getNav(
+//     [
+//         'options' => [
 //             'type'=>'tabs',
 //             'justified'=>1,
-//         ),
-//         'tabs' => array(
+//         ],
+//         'tabs' => [
 //             'tab 1'=>'Inhalt #1',
 //             'tab 2'=>'Inhalt #2',
-//         ),
-//     )
+//         ],
+//     ]
 // );")
 //    . addHtmltestTest("Tabs", "\$oHtml->getNav(
-//     array(
-//         'options' => array(
+//     [
+//         'options' => [
 //             'type'=>'pills',
 //             'stacked'=>1,
-//         ),
-//         'tabs' => array(
+//         ],
+//         'tabs' => [
 //             'tab 1'=>'Inhalt #1',
 //             'tab 2'=>'Inhalt #2',
-//         ),
-//     )
+//         ],
+//     ]
 // );")
-   .  addHtmltestTest("Tabelle", "\$oHtml->getTable(
-    array(
-        'header'=>array('A', 'B'), 
-        'body'=>array(
-            array('Zelle A 1', 'Zelle B 1'),
-            array('Zelle A 2', 'Zelle B 2'),
-            array('Zelle A 3', 'Zelle B 3'),
-        ),
-));"
-)
+    . addHtmltestTest(
+        "Tabelle",
+        "\$oHtml->getTable(
+    [
+        'header'=>['A', 'B'], 
+        'body'=>[
+            ['Zelle A 1', 'Zelle B 1'],
+            ['Zelle A 2', 'Zelle B 2'],
+            ['Zelle A 3', 'Zelle B 3'],
+        ],
+    ]);"
+    )
 ;
 
 // ----------------------------------------------------------------------
 
 echo '<table><thead>'
     . '<tr>'
-        . '<th>Beschreibung</th>'
-        . '<th>PHP-Code</th>'
-        . '<th>Ausgabe</th>'
-        . '<th>Ausgabe-Code</th>'
+    . '<th>Beschreibung</th>'
+    . '<th>PHP-Code</th>'
+    . '<th>Ausgabe</th>'
+    . '<th>Ausgabe-Code</th>'
     . '</tr>'
-. '</thead><tbody>'
-    .$sRows
-. '</tbody></table>'
+    . '</thead><tbody>'
+    . $sRows
+    . '</tbody></table>'
 ;
diff --git a/public_html/deployment/pages/act_login.php b/public_html/deployment/pages/act_login.php
index 288dbe47f17e5a9269307362b07e9c96582513d1..577102c6346af7d4d777b123d964da700ebf7a5f 100644
--- a/public_html/deployment/pages/act_login.php
+++ b/public_html/deployment/pages/act_login.php
@@ -8,6 +8,8 @@
 
   ---------------------------------------------------------------------
   2015-04-21  Axel <axel.hahn@iml.unibe.ch>
+  ...
+  2024-09-03  Axel <axel.hahn@unibe.ch>  php8 only; added variable types; short array syntax
   ###################################################################### */
 
 require_once("./inc_functions.php");
@@ -31,16 +33,16 @@ if (!$oUser->getUsername() && array_key_exists('user', $aParams)) {
     $oUser->authenticate();
 }
 
-    // if user is logged in and credentials were sent: reload to remove post vars
-    if ($oUser->getUsername() && array_key_exists('user', $aParams)) {
-        if (array_key_exists("goback", $_SESSION)){
-            $sUrl=$_SESSION["goback"];
-            unset($_SESSION["goback"]);
-        } else {
-            $sUrl='/deployment';
-        }
-        header("location: $sUrl");
+// if user is logged in and credentials were sent: reload to remove post vars
+if ($oUser->getUsername() && array_key_exists('user', $aParams)) {
+    if (array_key_exists("goback", $_SESSION)) {
+        $sUrl = $_SESSION["goback"];
+        unset($_SESSION["goback"]);
+    } else {
+        $sUrl = '/deployment';
     }
+    header("location: $sUrl");
+}
 
 // ----------------------------------------------------------------------
 // show infos or login form
@@ -52,63 +54,63 @@ if ($oUser->getUsername()) {
     // ------------------------------------------------------------
     // show user and roles + logoff
     // ------------------------------------------------------------
-    $sGrouplist='';
-    foreach ($oUser->getUserGroups() as $sGroupname){
-        $sGrouplist.=$oHtml->getIcon('user-group').$sGroupname . '<br>';
+    $sGrouplist = '';
+    foreach ($oUser->getUserGroups() as $sGroupname) {
+        $sGrouplist .= $oHtml->getIcon('user-group') . $sGroupname . '<br>';
     }
-    $sPermlist='';
-    foreach ($oUser->getUserPermission() as $sPerm){
-        $sPermlist.=$oHtml->getIcon('user-permission').$sPerm . '<br>';
+    $sPermlist = '';
+    foreach ($oUser->getUserPermission() as $sPerm) {
+        $sPermlist .= $oHtml->getIcon('user-permission') . $sPerm . '<br>';
     }
-    
-    $sOut.=''
-            . '<h2>' . t("page-login-profile") . '</h2>'
-            .'<table>
+
+    $sOut .= ''
+        . '<h2>' . t("page-login-profile") . '</h2>'
+        . '<table>
                 <tr>
-                    <td>'.t("page-login-userloggedin") . '</td>
+                    <td>' . t("page-login-userloggedin") . '</td>
                     <td><strong>' . $oUser->getUsername() . '</strong></td>
                 </tr>
                 <tr>
-                    <td>'.t("page-login-usergroups").'</td>
-                    <td>'.$sGrouplist . '</td>
+                    <td>' . t("page-login-usergroups") . '</td>
+                    <td>' . $sGrouplist . '</td>
                 </tr>
                 <tr>
-                    <td>'.t("page-login-userperms").'</td>
-                    <td>'.$sPermlist . '</td>
+                    <td>' . t("page-login-userperms") . '</td>
+                    <td>' . $sPermlist . '</td>
                 </tr>
             </table>'
-                . '<div style="clear: both; margin-bottom: 1em;"></div>'
-                . ' ' . aPrjHome() . ' '
-                . '<a href="?logoff=1" class="btn btn-danger">' . $oHtml->getIcon('poweroff') . t('logoff') . '</a>'
-            
-                // . '<br><br>INFO: <pre style="">roles:<br>' . print_r($oUser->getUserPermission(), true) . '</pre>'
-            . '</p>'
-            ;
+        . '<div style="clear: both; margin-bottom: 1em;"></div>'
+        . ' ' . aPrjHome() . ' '
+        . '<a href="?logoff=1" class="btn btn-danger">' . $oHtml->getIcon('poweroff') . t('logoff') . '</a>'
+
+        // . '<br><br>INFO: <pre style="">roles:<br>' . print_r($oUser->getUserPermission(), true) . '</pre>'
+        . '</p>'
+    ;
 } else {
     // ------------------------------------------------------------
     // login page
     // ------------------------------------------------------------
-    if (!isset($_SESSION['goback']) || true){
-        $sUrlback=$_SERVER["HTTP_REFERER"];
-        if(
-           strpos($sUrlback, $_SERVER["SERVER_NAME"])>0
-           && !strpos($sUrlback, '/all/login/')
-        ){
-            $_SESSION["goback"]=$sUrlback;
+    if (!isset($_SESSION['goback']) || true) {
+        $sUrlback = $_SERVER["HTTP_REFERER"];
+        if (
+            strpos($sUrlback, $_SERVER["SERVER_NAME"]) > 0
+            && !strpos($sUrlback, '/all/login/')
+        ) {
+            $_SESSION["goback"] = $sUrlback;
         }
     }
     $i = 0;
-    require_once ("./classes/formgen.class.php");
+    require_once("./classes/formgen.class.php");
 
-    $aForms = array(
-        'login' => array(
-            'meta' => array(
+    $aForms = [
+        'login' => [
+            'meta' => [
                 'method' => 'POST',
                 'action' => '?',
-            ),
-            'validate' => array(),
-            'form' => array(
-                'input' . $i++ => array(
+            ],
+            'validate' => [],
+            'form' => [
+                'input' . $i++ => [
                     'type' => 'text',
                     'name' => 'user',
                     'label' => t('page-login-username'),
@@ -119,8 +121,8 @@ if ($oUser->getUsername()) {
                     'placeholder' => t('page-login-username'),
                     'autofocus' => 'autofocus',
                     // 'inline' => '1',
-                ),
-                'input' . $i++ => array(
+                ],
+                'input' . $i++ => [
                     'type' => 'password',
                     'name' => 'password',
                     'label' => t('page-login-password'),
@@ -130,41 +132,41 @@ if ($oUser->getUsername()) {
                     'value' => $aParams['password'],
                     'placeholder' => t('page-login-password'),
                     // 'inline' => '1',
-                ),
-                'input' . $i++ => array(
+                ],
+                'input' . $i++ => [
                     'type' => 'submit',
-                    'label' => "",
                     'name' => 'btnsave',
                     'label' => t("login"),
                     'value' => $oHtml->getIcon('sign-ok') . t("login"),
-                ),
-            ),
-        )
-    );
+                ],
+            ],
+        ]
+    ];
 
     $oForm = new formgen($aForms);
-    $sOut.=''
-            . '<h2>' . t("page-login-info") . '</h2>'
-            . '<p>' . t("page-login-info-introtext") . '</p>'
-            . (array_key_exists('user', $aParams)
-                ? '<div class="alert alert-danger" role="alert">'.t('page-login-auth-failed').'</div>'
-                : ''
-            )
-            .$oForm->renderHtml("login");    
+    $sOut .= ''
+        . '<h2>' . t("page-login-info") . '</h2>'
+        . '<p>' . t("page-login-info-introtext") . '</p>'
+        . (array_key_exists('user', $aParams)
+            ? '<div class="alert alert-danger" role="alert">' . t('page-login-auth-failed') . '</div>'
+            : ''
+        )
+        . $oForm->renderHtml("login");
 }
 // $sOut.= '<div id="navbuttom">' . aPrjHome() . '</div>';
 
 // -- Ausgabe
 // echo $sOut;
-$BODY=
-$renderAdminLTE->addRow(
-    $renderAdminLTE->addCol('',3)
-    .$renderAdminLTE->addCol(
-        $renderAdminLTE->getCard([
-        'type'=>'',
-        'variant'=>'outline',
-        'text'=>$sOut,
-        ]),5
-    )
-    .$renderAdminLTE->addCol('',4)
-);
+$BODY =
+    $renderAdminLTE->addRow(
+        $renderAdminLTE->addCol('', 3)
+        . $renderAdminLTE->addCol(
+            $renderAdminLTE->getCard([
+                'type' => '',
+                'variant' => 'outline',
+                'text' => $sOut,
+            ]),
+            5
+        )
+        . $renderAdminLTE->addCol('', 4)
+    );
diff --git a/public_html/deployment/pages/act_overview.php b/public_html/deployment/pages/act_overview.php
index d96cbb833048d1e278210baa793dacd7b1f1fe0b..b6d0f8604dce0045dd6a20a523752e62f1f59a62 100644
--- a/public_html/deployment/pages/act_overview.php
+++ b/public_html/deployment/pages/act_overview.php
@@ -9,10 +9,12 @@
  * for a single project
 
   ---------------------------------------------------------------------
-  2014-11-17  Axel <axel.hahn@iml.unibe.ch>  added tags and branches
+  2013-11-08  Axel <axel.hahn@iml.unibe.ch>
   2014-04-24  Axel <axel.hahn@iml.unibe.ch>  new visual; sortorder in 
               project overview
-  2013-11-08  Axel <axel.hahn@iml.unibe.ch>
+  2014-11-17  Axel <axel.hahn@iml.unibe.ch>  added tags and branches
+  ...
+  2024-09-03  Axel <axel.hahn@unibe.ch>  php8 only; added variable types; short array syntax
   ###################################################################### */
 
 
@@ -24,79 +26,80 @@ if (!array_key_exists("prj", $aParams)) {
     require_once("./classes/projectlist.class.php");
     $oPrjList = new projectlist();
 
-    $BODY_END='<script type="text/javascript" src="/deployment/js/functions_overview.js"></script>';
+    $BODY_END = '<script type="text/javascript" src="/deployment/js/functions_overview.js"></script>';
     $BODY = $oPrjList->renderOverview();
 } else {
-    
+
     // ----------------------------------------------------------------------
     // overview of a single project
     // ----------------------------------------------------------------------
     require_once("./classes/project_gui.class.php");
     $oPrj = new projectgui($aParams["prj"]);
-    
-    $iCountOfBranches=0;
-    $iCountOfBuildErrors=count($oPrj->getBuildErrors());
-    $iCountOfpackages=count($oPrj->getVersions());
-    $iCountOfPhases=count($oPrj->getActivePhases());
-
-    $sBuildErrorContent='<p>'.t('build-failes-hint').'</p>';
-    if($iCountOfBuildErrors){
-        $aTabdata=array();
-        $aErrorfiles=$oPrj->getBuildErrors();
+
+    $iCountOfBranches = 0;
+    $iCountOfBuildErrors = count($oPrj->getBuildErrors());
+    $iCountOfpackages = count($oPrj->getVersions());
+    $iCountOfPhases = count($oPrj->getActivePhases());
+
+    $sBuildErrorContent = '<p>' . t('build-failes-hint') . '</p>';
+    if ($iCountOfBuildErrors) {
+        $aTabdata = [];
+        $aErrorfiles = $oPrj->getBuildErrors();
         rsort($aErrorfiles);
-        foreach ($aErrorfiles as $sNumber=>$sErrorfile){
-            $aTabdata[$oHtml->getIcon('file-any').' '.$sErrorfile]=''
-                    // . 'file: ' . $oHtml->getIcon('file-target'). ''.$sErrorfile.'<br>'
-                    // . 'date: ' . .'<br>'
-                    // . 'age: ' .  .'<br>'
-                    . $oPrj->getBuildErrorContent($sErrorfile);
+        foreach ($aErrorfiles as $sNumber => $sErrorfile) {
+            $aTabdata[$oHtml->getIcon('file-any') . ' ' . $sErrorfile] = ''
+                // . 'file: ' . $oHtml->getIcon('file-target'). ''.$sErrorfile.'<br>'
+                // . 'date: ' . .'<br>'
+                // . 'age: ' .  .'<br>'
+                . $oPrj->getBuildErrorContent($sErrorfile);
         }
         /*
-        $sBuildErrorContent.=$oHtml->getNav(array(
-                'options' => array(
+        $sBuildErrorContent.=$oHtml->getNav([
+                'options' => [
                     'type'=>'pills',
                     'justified'=>0,
-                ),
-                'tabs'=>$aTabdata)
+                ],
+                'tabs'=>$aTabdata
+            ]
             );
         */
-        $sBuildErrorContent.=$renderAdminLTE->getTabbedContent(['tabs'=>$aTabdata]);
+        $sBuildErrorContent .= $renderAdminLTE->getTabbedContent(['tabs' => $aTabdata]);
     } else {
-        $sBuildErrorContent=$oHtml->getBox('info', t('build-failes-none'));
+        $sBuildErrorContent = $oHtml->getBox('info', t('build-failes-none'));
     }
 
-    $sListOfBranches='';
-    foreach($oPrj->getRemoteBranches() as $aBranch){
-        $sListOfBranches.='<li title="'.$aBranch['revision'].'">'.$aBranch['label'] . '</li>';
+    $sListOfBranches = '';
+    foreach ($oPrj->getRemoteBranches() as $aBranch) {
+        $sListOfBranches .= '<li title="' . $aBranch['revision'] . '">' . $aBranch['label'] . '</li>';
         $iCountOfBranches++;
     }
-    if($sListOfBranches){
-        $sListOfBranches='<h4>'.t('branch-select').'</h4><ol>'.$sListOfBranches.'</ol>';
+    if ($sListOfBranches) {
+        $sListOfBranches = '<h4>' . t('branch-select') . '</h4><ol>' . $sListOfBranches . '</ol>';
     }
 
     // --- generate tabs:
 
-    $aTabdata=[];
+    $aTabdata = [];
     $aTabdata[$oHtml->getIcon('workflow') . t("way-of-packages")] = $oPrj->renderVisual();
-    if($iCountOfBuildErrors){
-        $aTabdata[$oHtml->getIcon('sign-error') . t("build-failes") . ($iCountOfBuildErrors ? ' <span class="badge badge-danger">'.$iCountOfBuildErrors.'</span>' : '' )]='<br>'.$sBuildErrorContent;
+    if ($iCountOfBuildErrors) {
+        $aTabdata[$oHtml->getIcon('sign-error') . t("build-failes") . ($iCountOfBuildErrors ? ' <span class="badge badge-danger">' . $iCountOfBuildErrors . '</span>' : '')] = '<br>' . $sBuildErrorContent;
     }
-    if($iCountOfpackages){
-        $aTabdata[$oHtml->getIcon('package') . t("archive") . ($iCountOfpackages ? ' <span class="badge badge-secondary">'.$iCountOfpackages.'</span>' : '' )] = '<br>'.$oPrj->renderVersionUsage();
+    if ($iCountOfpackages) {
+        $aTabdata[$oHtml->getIcon('package') . t("archive") . ($iCountOfpackages ? ' <span class="badge badge-secondary">' . $iCountOfpackages . '</span>' : '')] = '<br>' . $oPrj->renderVersionUsage();
     }
-    if($iCountOfPhases){
-        $aTabdata[$oHtml->getIcon('phase') . t('phases') .' '. ($iCountOfPhases ? '<span class="badge badge-secondary">'.$iCountOfPhases.'</span>' : '<span class="badge badge-danger">0</span>' )]='<br>'
-         .'<p>' 
-            . t("page-overview-phase-infos") 
-            . '</p>' 
+    if ($iCountOfPhases) {
+        $aTabdata[$oHtml->getIcon('phase') . t('phases') . ' ' . ($iCountOfPhases ? '<span class="badge badge-secondary">' . $iCountOfPhases . '</span>' : '<span class="badge badge-danger">0</span>')] = '<br>'
+            . '<p>'
+            . t("page-overview-phase-infos")
+            . '</p>'
             . $oPrj->renderPhaseInfo()
         ;
     }
 
     $sOut = ''
-            .'<h3>'.t('overview-label').'&nbsp;&nbsp;&nbsp;'.$oPrj->renderLink("setup").'</h3><br>'
-            .$renderAdminLTE->getTabbedContent(['tabs'=>$aTabdata])
-            ;
+        . '<h3>' . t('overview-label') . '&nbsp;&nbsp;&nbsp;' . $oPrj->renderLink("setup") . '</h3><br>'
+        . $renderAdminLTE->getTabbedContent(['tabs' => $aTabdata])
+    ;
 
     /*
     $sPhaselinks='';
@@ -110,15 +113,16 @@ if (!array_key_exists("prj", $aParams)) {
     }
     */
 
-    
-    $BODY=
+
+    $BODY =
         $renderAdminLTE->addRow(
             $renderAdminLTE->addCol(
                 $renderAdminLTE->getCard([
-                'type'=>'gray',
-                'variant'=>'outline',
-                'text'=>$sOut,
-                ]), 12
+                    'type' => 'gray',
+                    'variant' => 'outline',
+                    'text' => $sOut,
+                ]),
+                12
             )
             /*
             $renderAdminLTE->addCol(
@@ -146,7 +150,7 @@ if (!array_key_exists("prj", $aParams)) {
             )
             */
         );
-        // $sOut.='<div id="navbuttom">' . aGotop() . aHome() . '</div>';
+    // $sOut.='<div id="navbuttom">' . aGotop() . aHome() . '</div>';
 }
 // 
 echo $sOut;
diff --git a/public_html/deployment/pages/act_phase.php b/public_html/deployment/pages/act_phase.php
index bbebeddb0d287258150b81657915196b299a3fe0..947583b56b887e3654cd38ae5291340f3eef0356 100644
--- a/public_html/deployment/pages/act_phase.php
+++ b/public_html/deployment/pages/act_phase.php
@@ -10,6 +10,7 @@
   ---------------------------------------------------------------------
   2013-11-08  Axel <axel.hahn@iml.unibe.ch>
   2024-02-28  Axel <axel.hahn@unibe.ch>       remove foreman; update replacements
+  2024-09-03  Axel <axel.hahn@unibe.ch>       php8 only; added variable types; short array syntax
   ###################################################################### */
 
 require_once("./classes/project_gui.class.php");
@@ -25,7 +26,7 @@ if (isset($aParams["par3"])) {
 }
 
 if ($sPhase) {
-    $aWarnings=array();
+    $aWarnings=[];
     $sOutReplace='';
     
     // ----------------------------------------------------------------------
@@ -65,14 +66,14 @@ if ($sPhase) {
         
         // open all/ close all
         if(count($aReplacements)>1){
-            $sOutReplace.=$oHtml->getLinkButton(array(
+            $sOutReplace.=$oHtml->getLinkButton([
                 'onclick'=>'$(\'.divfileinfos\').slideDown(); $(\'.expandable\').removeClass(\'closed\'); this.blur(); return false;',
                 'icon'=>$oHtml->getIconClass('box-down'),
-            ))
-            .$oHtml->getLinkButton(array(
+            ])
+            .$oHtml->getLinkButton([
                 'onclick'=>'$(\'.divfileinfos\').slideUp(); $(\'.expandable\').addClass(\'closed\'); this.blur(); return false;',
                 'icon'=>$oHtml->getIconClass('box-up'),
-            ))
+            ])
             ;
         }
 
@@ -86,12 +87,12 @@ if ($sPhase) {
             $sOutReplace.='<h4>' . 
 
                 // --- link filename of template to toggle details.
-                    $oHtml->getLink(array(
+                    $oHtml->getLink([
                         'onclick'=>'$(\'#'.$sDivIdFile.'\').slideToggle(); $(this).toggleClass(\'closed\'); return false;',
                         'class'=>'expandable closed',
                         'icon'=>'templatefile',
                         'label'=>$tTplFile,
-                    )) . '</h4>'
+                    ]) . '</h4>'
                     . '<div id="'.$sDivIdFile.'" class="divfileinfos" style="display: none;">'
                     ;
 
diff --git a/public_html/deployment/pages/act_setup.php b/public_html/deployment/pages/act_setup.php
index e3c8df850e66fa64b8d44bbbe47e96d8d5cf2f32..602bad1c3fde6c49091669a1a742a5c5fb916ac1 100644
--- a/public_html/deployment/pages/act_setup.php
+++ b/public_html/deployment/pages/act_setup.php
@@ -10,62 +10,67 @@
 
   ---------------------------------------------------------------------
   2013-11-08  Axel <axel.hahn@iml.unibe.ch>
+  ...
+  2024-09-03  Axel <axel.hahn@unibe.ch>  php8 only; added variable types; short array syntax
   ###################################################################### */
 
 require_once("./classes/project_gui.class.php");
 require_once("./inc_functions.php");
 $sOut = '';
 
-$sFakePassword='********************';
+$sFakePassword = '********************';
 
 // items to mask
-$aMask=array(
-    'auth'=>array(
-        'ldap'=>array(
-            'PwLdapUser'=>$sFakePassword
-        )
-    ),
-    'foreman'=>array(
-        'password'=>$sFakePassword
-    ),
-    'projects'=>array(
-        'ldap'=>array(
-            'PwLdapUser'=>$sFakePassword
-        )
-    ),
-);
+$aMask = [
+    'auth' => [
+        'ldap' => [
+            'PwLdapUser' => $sFakePassword
+        ]
+    ],
+    'foreman' => [
+        'password' => $sFakePassword
+    ],
+    'projects' => [
+        'ldap' => [
+            'PwLdapUser' => $sFakePassword
+        ]
+    ],
+];
 
 /**
  * hide entries in config array
  * @param array $aMask
  * @param array $aConfig
- * @return type
+ * @return array
  */
-function maskEntries($aMask, $aConfig){
-    foreach ($aMask as $sKey=>$aValue){
-        
-        if (array_key_exists($sKey, $aConfig)){
-            $aConfig[$sKey]=(is_array($aValue)
-                    ? maskEntries($aMask[$sKey], $aConfig[$sKey])
-                    : $aMask[$sKey]
+function maskEntries(array $aMask, array $aConfig): array
+{
+    $aReturn = $aConfig;
+    foreach ($aMask as $sKey => $aValue) {
+
+        if (array_key_exists($sKey, $aReturn)) {
+            $aReturn[$sKey] = (is_array($aValue)
+                ? maskEntries($aMask[$sKey], $aReturn[$sKey])
+                : $aMask[$sKey]
             )
             ;
         }
     }
-    return $aConfig;
+    return $aReturn;
 }
 
 /**
- * recursive replace of values in a hash
+ * Recursive replace of values in a hash
  * source: https://www.w3schools.in/php-script/recursive-array-replace-by-Key-or-Value/
  * FIX: 3x "=" in if($Key === $Find) 
  * 
  * @param array   $Array    Array
  * @param string  $Find     key to scan for
- * @param strin   $Replace  new value
+ * @param string  $Replace  new value
  * @return array
  */
-function ArrayReplace($Array, $Find, $Replace) {
+function ArrayReplace(array $Array, string $Find, string $Replace): array
+{
     if (is_array($Array)) {
         foreach ($Array as $Key => $Val) {
             if (is_array($Array[$Key])) {
@@ -85,119 +90,114 @@ function ArrayReplace($Array, $Find, $Replace) {
 // ---------------------------------------------------------------------
 
 if ($aParams["prj"] == "all") {
-    
+
     // ------------------------------------------------------------
     // general setup - overview
     // ------------------------------------------------------------
     if (!array_key_exists("par3", $aParams)) {
         $oPrj = new project();
         // $aTmp=maskEntries($aMask, $aConfig);
-        $aTmp=$aConfig;
-        $aTmp=ArrayReplace($aTmp, "password",   $sFakePassword);
-        $aTmp=ArrayReplace($aTmp, "PwLdapUser", $sFakePassword);
-        $TITLE=t('menu-settings');
+        $aTmp = $aConfig;
+        $aTmp = ArrayReplace($aTmp, "password", $sFakePassword);
+        $aTmp = ArrayReplace($aTmp, "PwLdapUser", $sFakePassword);
+        $TITLE = t('menu-settings');
+
+        $sOut .= '<pre>' . print_r($aTmp, 1) . '</pre>';
 
-        $sOut.= '<pre>'.print_r($aTmp, 1).'</pre>';
-        
         // print_r($aConfig);
-        
+
         // ----- from HERE: generate form (experimental)
         $i = 0;
-        require_once ("./classes/formgen.class.php");
-        
+        require_once("./classes/formgen.class.php");
+
         // define editable options
-        $aMapping=array(
-            'general'=>array(
-                '["workDir"]'=>array('type'=>'text'),
-                '["versionsToKeep"]'=>array('type'=>'text', 'validate'=>'isinteger'),
-                '["builtsToKeep"]'=>array('type'=>'text', 'validate'=>'isinteger'),
-                '["lang"]'=>array('type'=>'text'),
-            ),
-        );
-        foreach ($aConfig['phases'] as $sPhase => $aPhaseData){
-            $aMapping['phase-'.$sPhase]=array(
-                '["phases"]["'.$sPhase.'"]["css"]["bgdark"]'=>array('type'=>'text'),
-                '["phases"]["'.$sPhase.'"]["css"]["bglight"]'=>array('type'=>'text'),
-                '["phases"]["'.$sPhase.'"]["css"]["bgbutton"]'=>array('type'=>'text'),
-            );
-            /*
-            if (array_key_exists("deploytimes", $aConfig["phases"][$sPhase])){
-                $aMapping['phase-'.$sPhase]['["phases"]["'.$sPhase.'"]["deploytimes"]']=array('type'=>'text');
-            }
-             * 
-             */
+        $aMapping = [
+            'general' => [
+                '["workDir"]' => ['type' => 'text'],
+                '["versionsToKeep"]' => ['type' => 'text', 'validate' => 'isinteger'],
+                '["builtsToKeep"]' => ['type' => 'text', 'validate' => 'isinteger'],
+                '["lang"]' => ['type' => 'text'],
+            ],
+        ];
+        foreach ($aConfig['phases'] as $sPhase => $aPhaseData) {
+            $aMapping['phase-' . $sPhase] = [
+                '["phases"]["' . $sPhase . '"]["css"]["bgdark"]' => ['type' => 'text'],
+                '["phases"]["' . $sPhase . '"]["css"]["bglight"]' => ['type' => 'text'],
+                '["phases"]["' . $sPhase . '"]["css"]["bgbutton"]' => ['type' => 'text'],
+            ];
+
         }
-        
-        $aForms = array(
-            'setup' => array(
-                'meta' => array(
+
+        $aForms = [
+            'setup' => [
+                'meta' => [
                     'method' => 'POST',
                     'action' => '?',
-                ),
-                'validate' => array(),
-                'form' => array(
-                    'input' . $i++ => array(
+                ],
+                'validate' => [],
+                'form' => [
+                    'input' . $i++ => [
                         'type' => 'hidden',
                         'name' => 'setupaction',
                         'value' => 'save',
-                    ),
-                ),
-            )
-        );
-        foreach ($aMapping as $sPartname=>$aPartData){
-            
+                    ],
+                ],
+            ]
+        ];
+        foreach ($aMapping as $sPartname => $aPartData) {
+
             // add a headline
-            $aForms['setup']['form']['input' . $i++] = array(
+            $aForms['setup']['form']['input' . $i++] = [
                 'type' => 'markup',
-                'value' => '<h3>'.t('setup-deployment-'.$sPartname).'</h3>',
-            );
-            
+                'value' => '<h3>' . t('setup-deployment-' . $sPartname) . '</h3>',
+            ];
+
             // add input items
-            foreach ($aPartData as $sName=>$aFormOptions){
-                
-                $sEval='$sCfgVal=$aConfig'.$sName.';';
-                eval($sEval);
-                
+            foreach ($aPartData as $sName => $aFormOptions) {
+
+                $sEval = '$sCfgVal=$aConfig' . $sName . ';';
+                eval ($sEval);
+
                 // checks
-                if (!$sCfgVal){
-                    $sError.='<li>configration variable $sConfig'.$sName.' does not exist.</li>';
+                if (!$sCfgVal) {
+                    $sError .= '<li>configration variable $sConfig' . $sName . ' does not exist.</li>';
                 }
                 // echo $sEval . ' .. ' . $sName . " :: " . $sCfgVar."<br>";
-                $sFormname=str_replace('"', '' , 'aConfig'.$sName);
+                $sFormname = str_replace('"', '', 'aConfig' . $sName);
+
 
-                
-                $aForms['setup']['form']['input' . $i++] = array(
-                    'value' => '<h3>'.t('setup-deployment-'.$sPartname).'</h3>',
+                $aForms['setup']['form']['input' . $i++] = [
+                    // 'value' => '<h3>' . t('setup-deployment-' . $sPartname) . '</h3>',
                     'type' => $aFormOptions['type'],
                     'name' => $sFormname,
-                    'label' => 'aConfig'.$sName,
+                    'label' => 'aConfig' . $sName,
                     'value' => $sCfgVal,
                     // 'required' => 'required',
                     'validate' => 'isastring',
                     'title' => htmlentities($sCfgVal),
                     'size' => 100,
                     'placeholder' => htmlentities($sCfgVal),
-                );
+                ];
             }
         }
-        if ($sError){
-            $sOut.=$oHtml->getBox("error", '<ul>'.$sError.'</ul>');
+        if ($sError) {
+            $sOut .= $oHtml->getBox("error", '<ul>' . $sError . '</ul>');
         } else {
             $oForm = new formgen($aForms);
             // TODO to enable form uncomment the next line
             // $sOut.=$oForm->renderHtml("setup");
         }
-        
+
     }
-        
+
     if (array_key_exists("par3", $aParams)) {
-        
+
         // ------------------------------------------------------------
         // setup a new project
         // ------------------------------------------------------------
-        if ($aParams["par3"]=="new") {
-            $TITLE=t("menu-new-project");
-            $sOut.= '<p>'.t("page-setup-info-new-project-introtext").'</p><hr>';
+        if ($aParams["par3"] == "new") {
+            $TITLE = t("menu-new-project");
+            $sOut .= '<p>' . t("page-setup-info-new-project-introtext") . '</p><hr>';
             $oPrj = new projectgui();
 
             if (array_key_exists("setupaction", $aParams) && $aParams["setupaction"] == "create") {
@@ -205,20 +205,21 @@ if ($aParams["prj"] == "all") {
                 if (!$sError) {
                     header("location: /deployment/" . $aParams["id"] . "/setup/");
                 }
-                $sOut.=$oHtml->getBox("error", $sError);
+                $sOut .= $oHtml->getBox("error", $sError);
             }
-            $sOut.=$oPrj->renderNewProject();
-            $BODY=
-            $renderAdminLTE->addRow(
-                $renderAdminLTE->addCol(
-                    $renderAdminLTE->getCard([
-                    'type'=>'primary',
-                    'variant'=>'outline',
-                    'text'=>$sOut,
-                    ]), 12
-                )
-            );
-    
+            $sOut .= $oPrj->renderNewProject();
+            $BODY =
+                $renderAdminLTE->addRow(
+                    $renderAdminLTE->addCol(
+                        $renderAdminLTE->getCard([
+                            'type' => 'primary',
+                            'variant' => 'outline',
+                            'text' => $sOut,
+                        ]),
+                        12
+                    )
+                );
+
         }
         // ------------------------------------------------------------
         // users and roles
@@ -232,7 +233,7 @@ if ($aParams["prj"] == "all") {
             $aUser2Roles=$oUserCfg->getUser2Roles();
             // $aUser2Projects=$oUserCfg->getUser2Projects();
             
-            $aUsers=array();
+            $aUsers=[];
             $sOut.=print_r($aUser2Roles,1).'<br>';
             $sRoles='';
             foreach ($aUser2Roles as $sRole=>$aUserlist){
@@ -260,84 +261,85 @@ if ($aParams["prj"] == "all") {
         // ------------------------------------------------------------
         // check lang-texts
         // ------------------------------------------------------------
-        if ($aParams["par3"]=="checklang") {
-            $TITLE=t("menu-checklang");
-            $sOut.='<p>'.t("page-setup-info-check-lang-intro").'</p>';
+        if ($aParams["par3"] == "checklang") {
+            $TITLE = t("menu-checklang");
+            $sOut .= '<p>' . t("page-setup-info-check-lang-intro") . '</p>';
             // $sOut.=print_r($aConfig, true);
-            $aTmp=array();
-            $aLangs=array();
-            
+            $aTmp = [];
+            $aLangs = [];
             // --- fetch data
             foreach (glob($aConfig["configDir"] . '/lang/*.json') as $filename) {
-                $sLang=basename($filename);
-                $aLangs[]=$sLang;
-                foreach (json_decode(file_get_contents($filename), true) as $skey => $sText){
-                    $aTmp[$skey][$sLang]=$sText;
+                $sLang = basename($filename);
+                $aLangs[] = $sLang;
+                foreach (json_decode(file_get_contents($filename), true) as $skey => $sText) {
+                    $aTmp[$skey][$sLang] = $sText;
                 }
             }
-            
+
             // --- generate output
-            $sWarnings='';
-            $sErrors='';
-            $sTable.='<table class="table"><thead><tr>'
-                    . '<th>key</th>';
-                foreach ($aLangs as $sLang) {
-                    $sTable.='<th>'.$sLang.'</th>';
-                }
-            $sTable.='</tr></thead><tbody>';
-            
-            foreach ($aTmp as $sKey => $aTexts){
-                $trid='tr'.md5($sKey);
-                $sTable.='<tr id="'.$trid.'"><td>'.$sKey.'</td>';
+            $sWarnings = '';
+            $sErrors = '';
+            $sTable .= '<table class="table"><thead><tr>'
+                . '<th>key</th>';
+            foreach ($aLangs as $sLang) {
+                $sTable .= '<th>' . $sLang . '</th>';
+            }
+            $sTable .= '</tr></thead><tbody>';
+
+            foreach ($aTmp as $sKey => $aTexts) {
+                $trid = 'tr' . md5($sKey);
+                $sTable .= '<tr id="' . $trid . '"><td>' . $sKey . '</td>';
                 foreach ($aLangs as $sLang) {
-                    if (array_key_exists($sLang, $aTmp[$sKey])){
-                        if (!$aTmp[$sKey][$sLang]){
-                            $sWarnings.='<li><a href="#'.$trid.'">'.$sLang.': key <em>'.$sKey.'</em> is empty.</a></li>';
-                            $sTable.='<td>'.t("empty").'</td>';
+                    if (array_key_exists($sLang, $aTmp[$sKey])) {
+                        if (!$aTmp[$sKey][$sLang]) {
+                            $sWarnings .= '<li><a href="#' . $trid . '">' . $sLang . ': key <em>' . $sKey . '</em> is empty.</a></li>';
+                            $sTable .= '<td>' . t("empty") . '</td>';
                         } else {
-                            $sTable.='<td>'.htmlentities($aTmp[$sKey][$sLang]).'</td>';
+                            $sTable .= '<td>' . htmlentities($aTmp[$sKey][$sLang]) . '</td>';
                         }
                     } else {
-                        $sErrors.='<li><a href="#'.$trid.'">'.$sLang.': key <em>'.$sKey.'</em> does not exist.</a></li>';
-                        $sTable.='<td class="error">!!! MISS !!!</td>';
+                        $sErrors .= '<li><a href="#' . $trid . '">' . $sLang . ': key <em>' . $sKey . '</em> does not exist.</a></li>';
+                        $sTable .= '<td class="error">!!! MISS !!!</td>';
                     }
                 }
-                $sTable.='</tr>';
+                $sTable .= '</tr>';
             }
-            $sTable.='</tbody></table>';
-            if ($sWarnings)$sWarnings='<ol class="warning">'.$sWarnings.'</ol>';
-            if ($sErrors)$sErrors='<ol class="error">'.$sErrors.'</ol>';
-            $sOut.=$sErrors.$sWarnings.$sTable;
+            $sTable .= '</tbody></table>';
+            if ($sWarnings)
+                $sWarnings = '<ol class="warning">' . $sWarnings . '</ol>';
+            if ($sErrors)
+                $sErrors = '<ol class="error">' . $sErrors . '</ol>';
+            $sOut .= $sErrors . $sWarnings . $sTable;
         }
         // ------------------------------------------------------------
         // logoanalyzer
         // ------------------------------------------------------------
-        if ($aParams["par3"]=="actionlog") {
+        if ($aParams["par3"] == "actionlog") {
             // $oPrj = new project();
-            $TITLE=t("menu-logs");
+            $TITLE = t("menu-logs");
             require_once("./classes/actionlog.class.php");
-            $oLog=new Actionlog('');
-            
-            $sOut.=$oLog->renderLogs(array(), true);
-            
+            $oLog = new Actionlog('');
+
+            $sOut .= $oLog->renderLogs([], true);
+
         }
         // ------------------------------------------------------------
         // stats
         // ------------------------------------------------------------
         // 
-        if ($aParams["par3"]=="stats") {
+        if ($aParams["par3"] == "stats") {
             // $oPrj = new project();
             require_once("./classes/actionlog.class.php");
-            $oLog=new Actionlog('');
-            
-            $sOut.='TODO'
-                    //. $oLog->renderStats(array(), true)
-                    ;
-            
+            $oLog = new Actionlog('');
+
+            $sOut .= 'TODO'
+                //. $oLog->renderStats([], true)
+            ;
+
         }
-        
+
     }
-    
+
 } else {
 
     // ------------------------------------------------------------
@@ -354,21 +356,22 @@ if ($aParams["prj"] == "all") {
     */
     if (array_key_exists("setupaction", $aParams) && $aParams["setupaction"] == "save") {
         if ($oPrj->saveConfig()) {
-            $sOut.=$oHtml->getBox("success", t("page-setup-info-settings-were-saved"));
+            $sOut .= $oHtml->getBox("success", t("page-setup-info-settings-were-saved"));
         } else {
-            $sOut.=$oHtml->getBox("error", t("page-setup-error-settings-were-not-saved"));
+            $sOut .= $oHtml->getBox("error", t("page-setup-error-settings-were-not-saved"));
         }
     }
-    $sErrors=$oPrj->renderErrorBoxes();
-    $sOut.= $sErrors ? $sErrors : $oPrj->renderProjectSetup();
-    $BODY=
+    $sErrors = $oPrj->renderErrorBoxes();
+    $sOut .= $sErrors ? $sErrors : $oPrj->renderProjectSetup();
+    $BODY =
         $renderAdminLTE->addRow(
             $renderAdminLTE->addCol(
                 $renderAdminLTE->getCard([
-                'type'=>'primary',
-                'variant'=>'outline',
-                'text'=>$sOut,
-                ]), 12
+                    'type' => 'primary',
+                    'variant' => 'outline',
+                    'text' => $sOut,
+                ]),
+                12
             )
         );
 
diff --git a/public_html/deployment/pages/act_valuestore.php b/public_html/deployment/pages/act_valuestore.php
index 3a35881fa7aa8a92a5d1f5c4d574115d1214acd3..df3c733b67f1107f4a4219041a4d780746e73357 100644
--- a/public_html/deployment/pages/act_valuestore.php
+++ b/public_html/deployment/pages/act_valuestore.php
@@ -6,38 +6,42 @@
 
   VALUESTORE
 
+  TODO: use queryparam.class.php
+
   ---------------------------------------------------------------------
   2023-11-24  Axel <axel.hahn@unibe.ch>
+  ...
+  2024-09-03  Axel <axel.hahn@unibe.ch>  php8 only; added variable types; short array syntax
   ###################################################################### */
 
 
-$TITLE=t('menu-valuestore');
+$TITLE = t('menu-valuestore');
 
 // ----------------------------------------------------------------------
 // functions
 // ----------------------------------------------------------------------
 
 /**
- * show an error message and quit with http status code 400 (Bad request)
+ * Show an error message and quit with http status code 400 (Bad request)
  * @param string  $sMessage  message to show
- * @return boolean
+ * @return void
  */
-function quit($sMessage)
+function quit(string $sMessage): void
 {
   $sProtocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
   header("$sProtocol 400: Bad request");
-  die("<h1>Bad request</h1>" . $sMessage);
-  return false;
+  die("<h1>Bad request</h1>$sMessage");
 }
 
 /**
- * get a request param from GET and POST scope (POST has priority) and
+ * Get a request param from GET and POST scope (POST has priority) and
  * verify it with execution of a cleanup array
+ * 
  * @param string  $sKey            key to search for in GET or POST
  * @param string  $sRegex4Cleanup  regex for filtering
- * @return type
+ * @return bool|string
  */
-function getParam($sKey, $sRegex4Cleanup = false)
+function getParam(string $sKey, string $sRegex4Cleanup = ''): bool|string
 {
   $sValue = false;
   if (array_key_exists($sKey, $_GET)) {
@@ -65,7 +69,7 @@ if (!$_GET || !count($_GET)) {
   quit("no parameter was found.");
 }
 
-foreach (array("action", "project") as $sKey) {
+foreach (["action", "project"] as $sKey) {
   if (!array_key_exists($sKey, $_GET)) {
       quit("value required: $sKey=");
   }
@@ -100,8 +104,8 @@ $sValue = getParam('value', '');
 $sOut = '';
 $sHeader = '';
 $sOut .= ''
-  .'<p>'.t('page-valuestore-hint').'</p>'
-  ;
+  . '<p>' . t('page-valuestore-hint') . '</p>'
+;
 require './../valuestore/classes/valuestore.class.php';
 
 $oVersion = new valuestore();
@@ -122,7 +126,7 @@ if (is_array($aData) && count($aData)) {
       // $sTable.='<td class="'.$sKey.'"><a href="'.$sUrl.'">'.$sValue.'</a></td>';
       $sOnclick = '';
       $sLabel = strstr($sValue, '"')
-        ? '<small>'.htmlentities($sValue).'</small>'
+        ? '<small>' . htmlentities($sValue) . '</small>'
         : '<a href="#" onclick="$(\'#eFilter\').val(\'' . $sValue . '\');filterTable();" title="click to filter by [' . $sValue . ']">' . htmlentities($sValue) . '</a>';
       $sTable .= '<td class="' . $sKey . '" ' . $sOnclick . '>' . $sLabel . '</td>' . "\n";
     }
diff --git a/public_html/deployment/plugins/build/tgz/build_tgz.php b/public_html/deployment/plugins/build/tgz/build_tgz.php
index 743a570b348326cc4ffe2a5766e70769c7d1e686..5ca737d12e5c782356bc04259a1caa191ab51036 100644
--- a/public_html/deployment/plugins/build/tgz/build_tgz.php
+++ b/public_html/deployment/plugins/build/tgz/build_tgz.php
@@ -5,28 +5,33 @@
  * Build plugin - TGZ
  * 
  * @author <axel.hahn@iml.unibe.ch>
+ * 
+ * 2024-09-02  Axel   php8 only; added variable types; short array syntax
  */
-class build_tgz extends build_base {
-    
+class build_tgz extends build_base
+{
+
     /**
      * check requirements if the plugin could work
      * @return array
      */
-    public function checkRequirements() {
+    public function checkRequirements(): array
+    {
         return [
             'which tar'
         ];
     }
-    
+
     /**
      * get an array with shell commands to execute
      * @return array
      */
-    public function getBuildCommands(){
+    public function getBuildCommands(): array
+    {
         return [
-            'cd "'. $this->getBuildDir(). '" && tar -czf "'. $this->getOutfile().'" .'
+            'cd "' . $this->getBuildDir() . '" && tar -czf "' . $this->getOutfile() . '" .'
         ];
     }
-   
+
 
 }
diff --git a/public_html/deployment/plugins/build/zip/build_zip.php b/public_html/deployment/plugins/build/zip/build_zip.php
index 2fadc359a115da1f020de2bb93ee7f625db53226..a36fcee0eb3e855e885e88d2ee846be282d59cb7 100644
--- a/public_html/deployment/plugins/build/zip/build_zip.php
+++ b/public_html/deployment/plugins/build/zip/build_zip.php
@@ -5,19 +5,23 @@
  * Build plugin - TGZ
  * 
  * @author <axel.hahn@iml.unibe.ch>
+ * 
+ * 2024-09-02  Axel   php8 only; added variable types; short array syntax
  */
-class build_zip extends build_base {
-    
+class build_zip extends build_base
+{
+
     /**
      * check requirements if the plugin could work
      * @return array
      */
-    public function checkRequirements() {
+    public function checkRequirements(): array
+    {
         return [
             'which zip'
         ];
     }
-    
+
     /**
      * get an array with shell commands to execute
      * used zip params:
@@ -26,11 +30,12 @@ class build_zip extends build_base {
      *   -r   recurse into directories
      * @return array
      */
-    public function getBuildCommands(){
+    public function getBuildCommands(): array
+    {
         return [
-            'cd "'. $this->getBuildDir(). '" && zip -9qr "'. $this->getOutfile().'" .'
+            'cd "' . $this->getBuildDir() . '" && zip -9qr "' . $this->getOutfile() . '" .'
         ];
     }
-   
+
 
 }
diff --git a/public_html/deployment/plugins/rollout/awx/rollout_awx.php b/public_html/deployment/plugins/rollout/awx/rollout_awx.php
index 5bb671355cb88fb4f93a3c59ecb482075be05902..5c791f2e7328cf9f7fdf990db0d3c84b5ee4421b 100644
--- a/public_html/deployment/plugins/rollout/awx/rollout_awx.php
+++ b/public_html/deployment/plugins/rollout/awx/rollout_awx.php
@@ -4,203 +4,234 @@
  * 
  * Rollout plugin - awx
  * 
- * Run an Https POST request to AWX
+ * Run an Https GET or POST request to AWX
  *
  * @author <axel.hahn@iml.unibe.ch>
+ * 
+ * 2024-09-02  Axel   php8 only; added variable types; short array syntax
  */
-class rollout_awx extends rollout_base {
-    
+class rollout_awx extends rollout_base
+{
+
     // url part for AWX API request to set count of results per page
-    protected $_sAwxApiPaging='&page_size=10000&page=1';
+    protected string $_sAwxApiPaging = '&page_size=10000&page=1';
 
     /**
-     * check requirements if the plugin could work
+     * Get an array with shell commands to check requirements if the plugin
+     * can work
+     * 
+     * @return array
      */
-    public function checkRequirements() {
-        // no specific checks needed ... always true
-        return true;
+    public function checkRequirements(): array
+    {
+        // no specific checks needed ... always empty
+        return [];
     }
-    
+
     /**
-     * check access to a deploy target
+     * Get an array with shell commands to check access to a deploy target
+     * 
+     * @return array
      */
-    public function checkConnectionToTarget() {
-        // do nothing ... always true
-        return true;
+    public function checkConnectionToTarget(): array
+    {
+        // do nothing ... always empty
+        return [];
     }
-    
+
     /**
-     * make an http get request and return the response body
+     * Make an http get request and return the response body
      * it is called by _makeRequest
      * $aRequest contains subkeys
-     * - url
-     * - method; one of GET|POST|PUT|DELETE
-     * - postdata; for POST only
+     * - url               relative urr; part behind api base url
+     * - method            one of GET|POST|PUT|DELETE
+     * - postdata          for POST only
+     * - ignore-ssl-error  flag: if true it willignores ssl verifiction (not recommended)
+     * - user, password    authentication with "user:password"
      * 
-     * @param array   $aRequest   arrayurl for Foreman API
-     * @return string
+     * @param array   $aRequest   array with request data
+     * @param integer $iTimeout   timeout in seconds
+     * @return array ... with subkeys "header" and "body" - or "error" if something went wrong
      */
-    protected function _httpRequest($aRequest=false, $iTimeout = 5) {
+    protected function _httpRequest(array $aRequest = [], int $iTimeout = 5): array
+    {
 
         if (!function_exists("curl_init")) {
             die("ERROR: PHP CURL module is not installed.");
         }
-        $aConfig=$this->getConfig();
-        
-        
-        $ch = curl_init($aConfig['url'].$aRequest['url']);
+        $aConfig = $this->getConfig();
+
+        $sAwxApiUrl=$aConfig['url'] . $aRequest['url'];
+        //   base url --^          ^-- relative url to api
+
+        $ch = curl_init($sAwxApiUrl);
 
         curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $aRequest['method']);
-        if ($this->_aRequest['method']==='POST'){
+        if ($aRequest['method'] === 'POST') {
             curl_setopt($ch, CURLOPT_POSTFIELDS, $aRequest['postdata']);
         }
 
-        if ($aConfig['user']){
-            curl_setopt($ch, CURLOPT_USERPWD, $aConfig['user'].':'.$aConfig['password']);
+        if ($aConfig['user']) {
+            curl_setopt($ch, CURLOPT_USERPWD, $aConfig['user'] . ':' . $aConfig['password']);
         }
 
-        if (isset($aConfig['ignore-ssl-error']) && $aConfig['ignore-ssl-error']){
+        if (isset($aConfig['ignore-ssl-error']) && $aConfig['ignore-ssl-error']) {
             curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
             curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
         }
-        
+
         curl_setopt($ch, CURLOPT_TIMEOUT, $iTimeout);
         curl_setopt($ch, CURLOPT_USERAGENT, 'IML Deployment :: rollout plugin awx ' . __CLASS__);
-        curl_setopt($ch, CURLOPT_HEADER,1);
-        curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
+        curl_setopt($ch, CURLOPT_HEADER, 1);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 
         $res = curl_exec($ch);
-        
-        $aReturn=array('info'=>curl_getinfo($ch), 'error'=>curl_error($ch));
+        if (!$res) {
+            $iErrorCode = curl_errno($ch);
+            $sErrorMsg = curl_error($ch);
+            curl_close($ch);
+            return [
+                'error' => "Failed to fetch $sAwxApiUrl - curl error #$iErrorCode: $sErrorMsg"
+            ];
+        }
+
+        $aReturn = ['info' => curl_getinfo($ch), 'error' => curl_error($ch)];
         curl_close($ch);
-        
-        $sHeader=substr($res, 0, $aReturn['info']['header_size']);
-        $aReturn['header']=explode("\n", $sHeader);
-        $aReturn['body']=str_replace($sHeader, "", $res);
+
+        $sHeader = substr($res, 0, $aReturn['info']['header_size']);
+        $aReturn['header'] = explode("\n", $sHeader);
+        $aReturn['body'] = str_replace($sHeader, "", $res);
 
         // print_r($aReturn);
         return $aReturn;
     }
 
     /**
-     * get AWX inventories and return them as array for select box
-     * [id] => array('value' => [ID], 'label' => [NAME] [ID])
-     * @return array
+     * Get AWX inventories and return them as array for select box
+     * [id] => ['value' => <ID>, 'label' => <NAME> <ID>]
+     * It returns false if the status code in the response is not 200
+     * 
+     * @return bool|array
      */
-    public function getAwxInventories(){
-        $aResponse=$this->_httpRequest(array(
-                'url'=>'/inventories/?order_by=name'.$this->_sAwxApiPaging,
-                'method'=>'GET',
-            )
+    public function getAwxInventories(): bool|array
+    {
+        $aResponse = $this->_httpRequest(
+            [
+                'url' => '/inventories/?order_by=name' . $this->_sAwxApiPaging,
+                'method' => 'GET',
+            ]
         );
 
-        if(!isset($aResponse['info']['http_code']) || $aResponse['info']['http_code']!==200){
+        if (!isset($aResponse['info']['http_code']) || $aResponse['info']['http_code'] !== 200) {
             return false;
         }
 
-        $aData=json_decode($aResponse['body'], 1);
-        $aReturn=[];
-        if (!$aData || !isset($aData['count'])){
-            $aReturn[]=[
-                'value'=>false,
-                'label'=>'!!! Access to awx api failed !!!'
+        $aData = json_decode($aResponse['body'], 1);
+        $aReturn = [];
+        if (!$aData || !isset($aData['count'])) {
+            $aReturn[] = [
+                'value' => false,
+                'label' => '!!! Access to awx api failed !!!'
             ];
             return $aReturn;
         }
-        if(count($aData['results']) < $aData['count']){
-            $aReturn[]=[
-                'value'=>false,
-                'label'=>'>>>>>>>>> WARNING: fetched ' . count($aData['results']) . ' of ' .$aData['count'] . ' items only'
+        if (count($aData['results']) < $aData['count']) {
+            $aReturn[] = [
+                'value' => false,
+                'label' => '>>>>>>>>> WARNING: fetched ' . count($aData['results']) . ' of ' . $aData['count'] . ' items only'
             ];
         }
 
-        foreach ($aData['results'] as $aItem){
-            $aReturn[$aItem['id']]= [
-                'value'=>$aItem['id'], 
-                'label'=>$aItem['name'].' (id: '.$aItem['id'].')'
+        foreach ($aData['results'] as $aItem) {
+            $aReturn[$aItem['id']] = [
+                'value' => $aItem['id'],
+                'label' => $aItem['name'] . ' (id: ' . $aItem['id'] . ')'
             ];
         }
 
-        return  $aReturn;
+        return $aReturn;
     }
     /**
      * get AWX Job Templates and return them as array for select box
-     * [id] => array('value' => [ID], 'label' => [PLAYBOOK] [ID])
-     * @return array
+     * [id] => ['value' => [ID], 'label' => [PLAYBOOK] [ID]]
+     * @return bool|array
      */
-    public function getAwxJobTemplates(){
-        $aResponse=$this->_httpRequest(array(
-                'url'=>'/job_templates/?order_by=name'.$this->_sAwxApiPaging,
-                'method'=>'GET',
-            )
+    public function getAwxJobTemplates()
+    {
+        $aResponse = $this->_httpRequest(
+            [
+                'url' => '/job_templates/?order_by=name' . $this->_sAwxApiPaging,
+                'method' => 'GET',
+            ]
         );
 
-        if(!isset($aResponse['info']['http_code']) || $aResponse['info']['http_code']!==200){
+        if (!isset($aResponse['info']['http_code']) || $aResponse['info']['http_code'] !== 200) {
             return false;
         }
 
-        $aData=json_decode($aResponse['body'], 1);
-        $aReturn=[];
-        if (!$aData || !isset($aData['count'])){
-            $aReturn[]=[
-                'value'=>false,
-                'label'=>'!!! Access to awx api failed !!!'
+        $aData = json_decode($aResponse['body'], 1);
+        $aReturn = [];
+        if (!$aData || !isset($aData['count'])) {
+            $aReturn[] = [
+                'value' => false,
+                'label' => '!!! Access to awx api failed !!!'
             ];
             return $aReturn;
         }
-        if(count($aData['results']) < $aData['count']){
-            $aReturn[]=[
-                'value'=>false,
-                'label'=>'>>>>>>>>> WARNING: fetched ' . count($aData['results']) . ' of ' .$aData['count'] . ' items only'
+        if (count($aData['results']) < $aData['count']) {
+            $aReturn[] = [
+                'value' => false,
+                'label' => '>>>>>>>>> WARNING: fetched ' . count($aData['results']) . ' of ' . $aData['count'] . ' items only'
             ];
         }
-        foreach ($aData['results'] as $aItem){
-            $aReturn[$aItem['id']]= [
-                'value'=>$aItem['id'], 
-                'label'=>$aItem['name'].' (id: '.$aItem['id'].'; '.$aItem['playbook'].')'
+        foreach ($aData['results'] as $aItem) {
+            $aReturn[$aItem['id']] = [
+                'value' => $aItem['id'],
+                'label' => $aItem['name'] . ' (id: ' . $aItem['id'] . '; ' . $aItem['playbook'] . ')'
             ];
         }
-        return  $aReturn;
+        return $aReturn;
     }
-    
+
 
     /**
-     * get array with commands to execute to deploy a package
+     * Get an array with shell commands to execute for deployment of built file
      * 
      * @param  string   $sPhase
      * @param  boolean  $bMask   Flag for public output; if true then mask your secrets
      * @return array
      */
-    public function getDeployCommands($sPhase, $bMask=false){
-        $aReturn=array();
-        $aConfig=$this->getConfig($sPhase, $bMask);
-        
+    public function getDeployCommands(string $sPhase, bool $bMask = false): array
+    {
+        $aReturn = [];
+        $aConfig = $this->getConfig($sPhase, $bMask);
+
         // ----- Checks:
-        $sCmdChecks='';
-        if(isset($aConfig['extravars']) && $aConfig['extravars']){
-            $aTmp=json_decode($aConfig['extravars'], 1);
-            if (!$aTmp || !is_array($aTmp) || !count($aTmp) ){
-                $sCmdChecks.='echo "ERROR: Value in extravars has wrong Syntax - this is no JSON: '.$aConfig['extravars'].'"; exit 1; ';                
+        $sCmdChecks = '';
+        if (isset($aConfig['extravars']) && $aConfig['extravars']) {
+            $aTmp = json_decode($aConfig['extravars'], 1);
+            if (!$aTmp || !is_array($aTmp) || !count($aTmp)) {
+                $sCmdChecks .= 'echo "ERROR: Value in extravars has wrong Syntax - this is no JSON: ' . $aConfig['extravars'] . '"; exit 1; ';
             }
-            $aConfig['extravars']=json_encode($aTmp);
+            $aConfig['extravars'] = json_encode($aTmp);
         }
-        
-        if(!isset($aConfig['inventory']) || !(int)$aConfig['inventory']){
-            $sCmdChecks.='echo "ERROR: no awx inventory was given."; exit 1; ';
+
+        if (!isset($aConfig['inventory']) || !(int) $aConfig['inventory']) {
+            $sCmdChecks .= 'echo "ERROR: no awx inventory was given."; exit 1; ';
         }
-        
+
         // ----- Send variables having values only
-        $aBodyvars=array();
-        foreach(['inventory'=>'inventory', 'limit'=>'limit', 'job_tags'=>'tags', 'extra_vars'=>'extravars'] as $sParam=>$sVarkey){
+        $aBodyvars = [];
+        foreach (['inventory' => 'inventory', 'limit' => 'limit', 'job_tags' => 'tags', 'extra_vars' => 'extravars'] as $sParam => $sVarkey) {
             if (isset($aConfig[$sVarkey]) && $aConfig[$sVarkey]) {
-                $aBodyvars[$sParam]=$aConfig[$sVarkey];
+                $aBodyvars[$sParam] = $aConfig[$sVarkey];
             }
         }
 
-        $sAuth=($aConfig['user'] ? '--user '.$aConfig['user'].':'.$aConfig['password'] : '');
-        $aReturn[]=$sCmdChecks . "curl -f -k -H 'Content-Type: application/json' -XPOST -d '". json_encode($aBodyvars, JSON_PRETTY_PRINT)."' $sAuth ".$aConfig['url']."/job_templates/".$aConfig['jobtemplate']."/launch/";
+        $sAuth = ($aConfig['user'] ? '--user ' . $aConfig['user'] . ':' . $aConfig['password'] : '');
+        $aReturn[] = $sCmdChecks . "curl -f -k -H 'Content-Type: application/json' -XPOST -d '" . json_encode($aBodyvars, JSON_PRETTY_PRINT) . "' $sAuth " . $aConfig['url'] . "/job_templates/" . $aConfig['jobtemplate'] . "/launch/";
         return $aReturn;
     }
-    
+
 
 }
diff --git a/public_html/deployment/plugins/rollout/default/rollout_default.php b/public_html/deployment/plugins/rollout/default/rollout_default.php
index dcb10bce8e79e9f33b871e5135ba92c42c6f8255..da7fc3fc9c91d23aa4ea5717516c96746dd33310 100644
--- a/public_html/deployment/plugins/rollout/default/rollout_default.php
+++ b/public_html/deployment/plugins/rollout/default/rollout_default.php
@@ -6,33 +6,44 @@
  * no action
  *
  * @author <axel.hahn@iml.unibe.ch>
+ * 
+ * 2024-09-02  Axel   php8 only; added variable types; short array syntax
  */
-class rollout_default extends rollout_base {
+class rollout_default extends rollout_base
+{
 
     /**
-     * check requirements if the plugin could work
+     * Get an array with shell commands to check requirements if the plugin
+     * can work
+     * 
+     * @return array
      */
-    public function checkRequirements(){
-        // no specific checks needed ... always true
-        return true;
+    public function checkRequirements(): array
+    {
+        // no specific checks needed ... always empty
+        return [];
     }
 
     /**
-     * check access to a deploy target
+     * Get an array with shell commands to check access to a deploy target
+     * 
+     * @return array
      */
-    public function checkConnectionToTarget(){
-        // do nothing ... always true
-        return true;
+    public function checkConnectionToTarget(): array
+    {
+        // do nothing ... always empty
+        return [];
     }
 
     /**
-     * get array with commands to execute to deploy a package
+     * Get an array with shell commands to execute for deployment of built file
      * 
      * @param  string   $sPhase
      * @param  boolean  $bMask   Flag for public output; if true then mask your secrets
      * @return array
      */
-    public function getDeployCommands($sPhase, $bMask=false){
+    public function getDeployCommands(string $sPhase, bool $bMask = false): array
+    {
         return [
             'echo "SKIP"'
         ];
@@ -44,17 +55,19 @@ class rollout_default extends rollout_base {
      * 
      * @return string
      */
-    public function renderFormdata4Project() {
+    public function renderFormdata4Project(): string
+    {
         return $this->_t('no-cfg');
     }
 
     /**
-     * override general form renderer: show a single message that no
-     * configuration items exist
-     * 
+     * override of form renderer: show configuration for a given phase
+     
+     * @param string $sPhase  phaese; one of preview|stage|live
      * @return string
      */
-    public function renderFormdata4Phase($sPhase) {
+    public function renderFormdata4Phase(string $sPhase): string
+    {
         return $this->_t('no-cfg');
-    }    
+    }
 }
diff --git a/public_html/deployment/plugins/rollout/ssh/rollout_ssh.php b/public_html/deployment/plugins/rollout/ssh/rollout_ssh.php
index 012fe813de13bf416271e4fb693f5ac941797a92..27e6c82dac4ca2474e949c26ff5440b47e27868d 100644
--- a/public_html/deployment/plugins/rollout/ssh/rollout_ssh.php
+++ b/public_html/deployment/plugins/rollout/ssh/rollout_ssh.php
@@ -7,34 +7,44 @@
  * Run a SSH command on remote targets
  *
  * @author <axel.hahn@iml.unibe.ch>
+ * 
+ * 2024-09-02  Axel   php8 only; added variable types; short array syntax
  */
 class rollout_ssh extends rollout_base {
 
     /**
-     * check requirements if the plugin could work
+     * Get an array with shell commands to check requirements if the plugin
+     * can work
+     * 
+     * @return array
      */
-    public function checkRequirements() {
-        // no specific checks needed ... always true
-        return true;
+    public function checkRequirements(): array 
+    {
+        // no specific checks needed ... always empty
+        return [];
     }
 
     /**
-     * check access to a deploy target
+     * Get an array with shell commands to check access to a deploy target
+     * 
+     * @return array
      */
-    public function checkConnectionToTarget() {
-        // do nothing ... always true
-        return true;
+    public function checkConnectionToTarget(): array
+    {
+        // do nothing ... always empty
+        return [];
     }
 
     /**
-     * get array with commands to execute to deploy a package
+     * Get an array with shell commands to execute for deployment of built file
      * 
-     * @param  string   $sPhase  phase
+     * @param  string   $sPhase
      * @param  boolean  $bMask   Flag for public output; if true then mask your secrets
      * @return array
      */
-    public function getDeployCommands($sPhase,$bMask=false){
-        $aReturn=array();
+    public function getDeployCommands(string $sPhase, bool $bMask = false): array
+    {
+        $aReturn=[];
         $aConfig=$this->getConfig($sPhase);
         
         // loop over hosts and create shell commands for it
diff --git a/public_html/deployment/plugins/shellcmd/load/plugin.php b/public_html/deployment/plugins/shellcmd/load/plugin.php
index 1eb0a12ca3ffb413ac78bfd4ca50ea70d75f7325..ff5fec241249d35797a285b321bc9c0166a1d598 100644
--- a/public_html/deployment/plugins/shellcmd/load/plugin.php
+++ b/public_html/deployment/plugins/shellcmd/load/plugin.php
@@ -4,26 +4,30 @@
  * SHELLCMD PLUGIN :: LOAD
  * 
  * ----------------------------------------------------------------------
- * 2022-08-05  axel.hahn@iml.unibe.ch
- * 2023-12-13  axel.hahn@unibe.ch      minified
+ * Axel: axel.hahn@unibe.ch
+ * 2022-08-05  Axel   initial version
+ * 2023-12-13  Axel   minified
+ * 2024-09-02  Axel   php8 only; added variable types; short array syntax
  */
-class shellcmd_load {
+class shellcmd_load
+{
     /**
      * parse output and extract wanted values in section "data"
      * @return array
      */
-    public function parsedata($aResult){
-        $aTmp1=array_reverse(explode(',', $aResult['output'][0]));
-        $aResult['data']=[
-            'uptime'=>(isset($aTmp1[5])
+    public function parsedata(array $aResult): array
+    {
+        $aTmp1 = array_reverse(explode(',', $aResult['output'][0]));
+        $aResult['data'] = [
+            'uptime' => (isset($aTmp1[5])
                 ? trim(substr($aTmp1[5], 10) . $aTmp1[4])
                 : trim(substr($aTmp1[4], 10))
-            ).' h'
+            ) . ' h'
             ,
-            'users'=>trim(str_replace(' users', '', $aTmp1[3])),
-            'load'=>trim(preg_replace('/^.*:/', '', $aTmp1[2])),
-            'load5'=>trim($aTmp1[1]),
-            'load15'=>trim($aTmp1[0]),
+            'users' => trim(str_replace(' users', '', $aTmp1[3])),
+            'load' => trim(preg_replace('/^.*:/', '', $aTmp1[2])),
+            'load5' => trim($aTmp1[1]),
+            'load15' => trim($aTmp1[0]),
         ];
         return $aResult;
     }
diff --git a/public_html/deployment/plugins/shellcmd/processes/plugin.php b/public_html/deployment/plugins/shellcmd/processes/plugin.php
index ef858fea181bb5731966db5839ad7be0776b8f52..ac6f0995883989902b9f0189c7ea3c2c4bb34762 100644
--- a/public_html/deployment/plugins/shellcmd/processes/plugin.php
+++ b/public_html/deployment/plugins/shellcmd/processes/plugin.php
@@ -4,17 +4,21 @@
  * SHELLCMD PLUGIN :: Processes
  * 
  * ----------------------------------------------------------------------
- * 2022-09-19  axel.hahn@iml.unibe.ch
- * 2023-12-13  axel.hahn@unibe.ch      minified
+ * Axel: axel.hahn@unibe.ch
+ * 2022-09-19  Axel   initial version
+ * 2023-12-13  Axel   minified
+ * 2024-09-02  Axel   php8 only; added variable types; short array syntax
  */
-class shellcmd_processes {
+class shellcmd_processes
+{
     /**
      * parse output and extract wanted values in section "data"
      * @return array
      */
-    public function parsedata($aResult){
-        $aResult['data']=[
-            'count'=>count($aResult['output'])-1,
+    public function parsedata(array $aResult): array
+    {
+        $aResult['data'] = [
+            'count' => count($aResult['output']) - 1,
         ];
         return $aResult;
     }
diff --git a/public_html/deployment/plugins/shellcmd/top/plugin.php b/public_html/deployment/plugins/shellcmd/top/plugin.php
index b0a5fa562b6c8a942c961017a9b00ad541469ac3..a9c20409bed8b96b0ec4889ac48d7bf2491c0fc0 100644
--- a/public_html/deployment/plugins/shellcmd/top/plugin.php
+++ b/public_html/deployment/plugins/shellcmd/top/plugin.php
@@ -4,17 +4,21 @@
  * SHELLCMD PLUGIN :: top
  * 
  * ----------------------------------------------------------------------
- * 2023-11-23  axel.hahn@unibe.ch
- * 2023-12-13  axel.hahn@unibe.ch      minified
+ * Axel: axel.hahn@unibe.ch
+ * 2023-11-23  Axel   initial version
+ * 2023-12-13  Axel   minified
+ * 2024-09-02  Axel   php8 only; added variable types; short array syntax
  */
-class shellcmd_top {
+class shellcmd_top
+{
     /**
      * parse output and extract wanted values in section "data"
      * @return array
      */
-    public function parsedata($aResult){
-        $aResult['data']=[
-            'count'=>count($aResult['output'])-1,
+    public function parsedata(array $aResult): array
+    {
+        $aResult['data'] = [
+            'count' => count($aResult['output']) - 1,
         ];
         return $aResult;
     }
diff --git a/public_html/valuestore/browse.php b/public_html/valuestore/browse.php
deleted file mode 100644
index f19da467f101fe776eb62d4062f8f8f0f81a793a..0000000000000000000000000000000000000000
--- a/public_html/valuestore/browse.php
+++ /dev/null
@@ -1,167 +0,0 @@
-<?php
-
-/**
- * 
- * VERSION HANDLER API
- * 
- * Syntax des Aufrufs (als GET Schreibweise; ein POST soll (später) auch gehen):
- * http://dev.ci.iml.unibe.ch:8002/versions/?action=update&project=ci&phase=preview&host=ci.preview.iml.unibe.ch&variable=version&value=007
- * 
- * Parameter:
- * 
- *      action=update (get-Funktionen muss ich später noch bauen)
- *      project=[Projekt-ID (=Name des Package, das installiert wird)]
- *      phase=(preview|stage|live)
- *      host=[FQDN]
- *      variable=version (andere noch nicht unterstützt/ vorgesehen)
- *      value=[Versionsdaten]
- * 
- * 2017-04-07  hahn  first lines
- */
-
-
-// ----------------------------------------------------------------------
-// functions
-// ----------------------------------------------------------------------
-
-/**
- * show an error message and quit with http status code 400 (Bad request)
- * @param string  $sMessage  message to show
- * @return boolean
- */
-function quit($sMessage) {
-    $sProtocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
-    header("$sProtocol 400: Bad request");
-    die("<h1>Bad request</h1>" . $sMessage);
-    return false;
-}
-
-/**
- * get a request param from GET and POST scope (POST has priority) and
- * verify it with execution of a cleanup array
- * @param string  $sKey            key to search for in GET or POST
- * @param string  $sRegex4Cleanup  regex for filtering
- * @return type
- */
-function getParam($sKey, $sRegex4Cleanup = false) {
-    $sValue=false;
-    if (array_key_exists($sKey, $_GET)) {
-        $sValue=$_GET[$sKey];
-    }
-    if (array_key_exists($sKey, $_POST)) {
-        $sValue=$_POST[$sKey];
-    }
-    if(!$sValue){
-        return false;
-    }
-    $sReturn = $sRegex4Cleanup ? preg_replace($sRegex4Cleanup, '', $sValue) : $sValue;
-
-    if ($sReturn !== $sValue) {
-        quit("ERROR: the parameter value in [$sKey = ...] has a wrong format.");
-    }
-    return $sReturn;
-}
-
-// ----------------------------------------------------------------------
-// check required params
-// ----------------------------------------------------------------------
-/*
-if (!$_GET || !count($_GET)) {
-    quit("no parameter was found.");
-}
-
-foreach (array("action", "project") as $sKey) {
-    if (!array_key_exists($sKey, $_GET)) {
-        quit("value required: $sKey=");
-    }
-}
- */
-
-
-// ----------------------------------------------------------------------
-// get vars
-// ----------------------------------------------------------------------
-
-
-$sAction = getParam('action', '/[^a-z]/');
-
-$sProject = getParam('project', '/[^a-z\-0-9]/');
-$sPhase = getParam('phase', '/[^a-z]/');
-$sPlace = getParam('place', '/[^a-z]/');
-$sHost = getParam('host', '/[^a-z\.\-0-9]/');
-
-if ($sHost && !$sPlace) {
-    $sPlace = 'installed';
-}
-
-$sVarname = getParam('variable', '/[^a-z]/');
-$sValue = getParam('value', '');
-
-
-// ----------------------------------------------------------------------
-// init class
-// ----------------------------------------------------------------------
-
-$sOut='';
-$sHeader='';
-$sOut.='<h1>data browser</h1>';
-require './classes/valuestore.class.php';
-
-$oVersion = new valuestore();
-
-$aData=$oVersion->dumpdb();
-$sTable='';
-$sTableHead='';
-if(is_array($aData) && count($aData)){
-    // echo '<pre>' . print_r($aData, 1);
-    foreach ($aData as $aRow){
-        $sItemUrl='index.php?action=show&project='.$aRow['project'].'&package='.$aRow['package'].'&phase='.$aRow['phase'].'&host='.$aRow['host'].'&variable='.$aRow['variable'].'';
-        $sTable.='<tr ondblclick="location.href=\''.$sItemUrl.'\'">';
-        // $sTable.='<tr>';
-        foreach ($aRow as $sKey=>$sValue){
-            // $sTable.='<td class="'.$sKey.'"><a href="'.$sUrl.'">'.$sValue.'</a></td>';
-            $sOnclick='';
-            $sLabel=strstr($sValue, '"')
-                    ? htmlentities($sValue)
-                    : '<a href="#" onclick="$(\'#eFilter\').val(\''.$sValue.'\');filterTable();" title="click to filter by ['.$sValue.']">'.htmlentities($sValue).'</a>'
-                    ;
-            $sTable.='<td class="'.$sKey.'" '.$sOnclick.'>'.$sLabel.'</td>'."\n";
-        }
-        $sTable.='<td>'
-                // . '<button onclick="location.href=\''.$sItemUrl.'&action=get\'">View</button>'
-                . '<form method="POST" action="'.$sItemUrl.'">'
-                    . '<input type="hidden" name="action" value="delete">'
-                    . '<button>Delete</button>'
-                . '</form>'
-                . '</td>'
-                . '</tr>'."\n";
-        
-    }
-    foreach (array_keys($aRow) as $sKey){
-        $sTableHead.='<th class="'.$sKey.'">'.$sKey.'</th>';
-    }
-    $sOut.=''
-            . '<form method="POST" action="index.php">'
-                . '<input type="hidden" name="action" value="cleanup">'
-                . '<button>Cleanup (older 1d)</button>'
-            . '</form><hr>'
-            
-            . '<table id="tbldata"><thead>'
-            . '<tr>'.$sTableHead. '<th>action</th></tr>'
-            . '</thead>'
-            . '<tbody>'.$sTable.'</tbody></table>';
-    
-} else {
-    $sOut.='no data yet.';
-}
-// $sOut.='<script>addFilterToTable("tbldata"); filterTable();</script>';
-
-require_once("../deployment/classes/page.class.php");
-$oPage = new Page();
-$oPage->addResponseHeader("Pragma: no-cache");
-$oPage->setOutputtype('html');
-$oPage->setHeader($sHeader);
-$oPage->addJsOnReady('addFilterToTable(); filterTable();');
-
-$oPage->setContent($sOut);
-echo $oPage->render();
diff --git a/public_html/valuestore/classes/valuestore.class.php b/public_html/valuestore/classes/valuestore.class.php
index 8110ba3824b292f61d758d71c6d59e530a441bdc..d31dc77f28e75c5d02e8630ffda14136c9f578bd 100644
--- a/public_html/valuestore/classes/valuestore.class.php
+++ b/public_html/valuestore/classes/valuestore.class.php
@@ -26,40 +26,84 @@
  *     $aVersions=$oVersion->deleteValues("version");
  * 
  * @author hahn
+ * 
+ * Axel: <axel.hahn@unibe.ch>
+ * 
+ * (...)
+ * 2024-09-04  Axel   php8 only; added variable types; short array syntax
  */
-class valuestore {
+class valuestore
+{
+
+    /**
+     * Project id
+     * @var string
+     */
+    public string $sProject = '';
+
+    /**
+     * Name of sPackage = project id in ci server
+     * @var string
+     */
+    public string $sPackage = '';
+
+    /**
+     * Phase; one of preview|stage|live
+     * @var string
+     */
+    public string $sPhase = '';
+
+    /**
+     * Name of place inside a phase; one of onhold|ready2install|deployed
+     * @var string
+     */
+    public string $sPlace = '';
+
+    /**
+     * Name of the host
+     * @var string
+     */
+    public string $sHost = '';
+
+    /**
+     * Name of the variable; eg. "version"
+     * @var string
+     */
+    public string $sVariable = '';
 
-    public $sProject = false;
-    public $sPackage = false;
-    public $sPhase = false;
-    public $sPlace = false;
-    public $sHost = false;
-    public $sVariable = false;
-    public $sData = false;
-    
-    protected $_bDebug = true;
+    /**
+     * Value of the variable
+     * @var string
+     */
+    public string $sData = '';
 
     /**
-     * filename of sqlite database file
-     * @var type 
+     * Flag: enable debug?
+     * @var bool
      */
-    private $_dbfile = false;
+    protected bool $_bDebug = true;
 
     /**
-     * database connection
+     * Filename of sqlite database file
+     * @var string
+     */
+    private string $_dbfile = '';
+
+    /**
+     * Database connection
      * @var object
      */
-    protected $_oDB = false;
+    protected object $_oDB;
 
     /**
      * TTL vor stored values - 1 day [sec]
      * @var integer
      */
-    protected $_iTTL = 86400;
+    protected int $_iTTL = 86400;
 
     /**
      * create statement for the database
-     * @var type 
+     * @var string
      */
     private $_sCreate = '
         CREATE TABLE "values" (
@@ -75,51 +119,71 @@ class valuestore {
           UNIQUE (project, package, phase, place, host, variable) ON CONFLICT REPLACE
         );'
     ;
-    
-    // hardcoded
-    protected $_allowedPhases=array('preview', 'stage', 'live');
-    protected $_allowedPlaces=array('onhold', 'ready2install', 'deployed');
-    
+
+    /**
+     * List of allowed phases
+     * hardcoded :-/
+     * 
+     * @var array
+     */
+    protected array $_allowedPhases = ['preview', 'stage', 'live'];
+
+    /**
+     * List of allowed places
+     * hardcoded :-/
+     * 
+     * @var array
+     */
+
+    protected array $_allowedPlaces = ['onhold', 'ready2install', 'deployed'];
+
     // ----------------------------------------------------------------------
     // CONSTRUCTOR
     // ----------------------------------------------------------------------
-    
+
     /**
-     * constructor ... no params
+     * Constructor ... no params
      */
-    public function __construct(){
-        
+    public function __construct()
+    {
+
         // cache dir is hardcoded to versions directory :-/
         $this->_dbfile = __DIR__ . '/../data/versioncache.db';
-        
+
         $this->_oDB = new PDO("sqlite:" . $this->_dbfile);
         if (!file_exists($this->_dbfile) || !filesize($this->_dbfile)) {
             $this->_createDb();
         }
     }
+
     /**
-     * add a log messsage
+     * Add a logging messsage with ahlogger
+     * 
      * @global object $oLog
+     * 
      * @param  string $sMessage  messeage text
      * @param  string $sLevel    warnlevel of the given message
      * @return bool
      */
-    private function log($sMessage, $sLevel = "info") {
+    private function log($sMessage, $sLevel = "info"): bool
+    {
         global $oCLog;
-        if($oCLog){
+        if ($oCLog) {
             return $oCLog->add(basename(__FILE__) . " class " . __CLASS__ . " - " . $sMessage, $sLevel);
         }
         return false;
     }
-    
+
     // ----------------------------------------------------------------------
     // PRIVATE 
     // ----------------------------------------------------------------------
 
     /**
      * create sqlite database - called in constructor if the file does not exist
+     * @return boolean
      */
-    private function _createDb() {
+    private function _createDb(): bool
+    {
         if (file_exists($this->_dbfile)) {
             echo $this->_bDebug ? "removing existing file $this->_dbfile ...<br>\n" : '';
             unlink($this->_dbfile);
@@ -129,116 +193,127 @@ class valuestore {
         $this->_oDB = new PDO("sqlite:" . $this->_dbfile);
         $this->_makeQuery($this->_sCreate);
         if (!file_exists($this->_dbfile)) {
-            $this->_quit(__FUNCTION__ , "ERROR: unable to create sqlite database " . $this->_dbfile);
+            $this->_quit(__FUNCTION__, "ERROR: unable to create sqlite database " . $this->_dbfile);
         }
         return true;
     }
 
     /**
-     * execute a sql statement
+     * Execute a sql statement
+     * It returns false if the query failed.
+     * On success you get a PDO result object
+     * 
      * @param string $sSql   sql statement
      * @param array  $aVars  array with values (uses PDO::prepare(); $sSql must contain placeholders :key)
-     * @return database object
+     * @return bool|object result object of query
      */
-    private function _makeQuery($sSql, $aVars=false) {
+    private function _makeQuery(string $sSql, array $aVars = []): bool|object
+    {
         // $this->_log(__FUNCTION__."($sSql)");
         // echo "DEBUG: executing SQL<pre>$sSql</pre>";
         $this->log("start query");
-        if ($aVars && is_array($aVars)){
+        if ($aVars && is_array($aVars)) {
             $oStatement = $this->_oDB->prepare($sSql);
             $result = $oStatement->execute($aVars);
         } else {
             $result = $this->_oDB->query($sSql);
         }
-        if(!$result){
-            $this->log('<pre>'.print_r($this->_oDB->errorInfo()).'</pre>', 'error');
+        if (!$result) {
+            $this->log('<pre>' . print_r($this->_oDB->errorInfo()) . '</pre>', 'error');
         }
-        $this->log("end query - ".$sSql);
+        $this->log("end query - " . $sSql);
         return $result;
     }
-    
+
     /**
-     * execute a sql statement
-     * @param string $sSql sql statement
+     * Execute a sql SELECT statement and get an array with results
+     * 
+     * @param string $sSql  sql statement
+     * @param string $sKey  optional: return only values of this key
      * @return array of resultset
      */
-    private function _makeSelectQuery($sSql, $aKey=false) {
+    private function _makeSelectQuery(string $sSql, string $sKey = ''): array
+    {
         // $this->_log(__FUNCTION__."($sSql)");
         // echo "DEBUG: executing select SQL<pre>$sSql</pre>";
         $this->log("start query");
         $oStatement = $this->_oDB->prepare($sSql);
         $oStatement->execute();
-        $aReturn=array();
+        $aReturn = [];
         while ($row = $oStatement->fetch(PDO::FETCH_ASSOC)) {
-          if ($aKey && array_key_exists($aKey, $row)){
-            $aReturn[] = $row[$aKey];
-          } else {
-            $aReturn[] = $row;
-          }
-        }        
-        $this->log("end query - ".$sSql);
+            if ($sKey && array_key_exists($sKey, $row)) {
+                $aReturn[] = $row[$sKey];
+            } else {
+                $aReturn[] = $row;
+            }
+        }
+        $this->log("end query - " . $sSql);
         return $aReturn;
     }
 
     /**
-     * log error and quit. it echoes the error message on screen if debug 
+     * Log error and quit. it echoes the error message on screen if debug 
      * is enabled.
+     * 
      * @param string  $sFunction  name of method that throws the error
      * @param string  $sMessage   error message
-     * @return boolean
+     * @return void
      */
-    private function _quit($sFunction, $sMessage){
+    private function _quit(string $sFunction, string $sMessage): void
+    {
         error_log(__CLASS__ . "::$sFunction - $sMessage " . "whereiam: " . print_r($this->whereiam(), 1));
-        if ($this->_bDebug){
+        if ($this->_bDebug) {
             echo __CLASS__ . "::$sFunction stopped.<br>\n"
-                    . "whereiam: <pre>" . print_r($this->whereiam(), 1)."</pre>"
-                    ;
+                . "whereiam: <pre>" . print_r($this->whereiam(), 1) . "</pre>"
+            ;
             die($sMessage);
         } else {
-            die("ERROR ... wrong usage of class ". __CLASS__);
+            die("ERROR ... wrong usage of class " . __CLASS__);
         }
-        return false;
     }
 
     // ----------------------------------------------------------------------
     // PUBLIC GETTER
     // ----------------------------------------------------------------------
-    
-    
+
+
     /**
-     * get list of current projects
-     * @return type
+     * Get list of current projects
+     * @return array
      */
-    public function getProjects(){
-        $sSql="select distinct(project) from `values`";
+    public function getProjects(): array
+    {
+        $sSql = "select distinct(project) from `values`";
         return $this->_makeSelectQuery($sSql, 'project');
     }
-    
+
     /**
-     * get list of current projects
-     * @return type
+     * Get list of current projects
+     * @return array
      */
-    public function getPackages(){
-        $sSql="select distinct(package) from `values`";
+    public function getPackages(): array
+    {
+        $sSql = "select distinct(package) from `values`";
         return $this->_makeSelectQuery($sSql, 'package');
     }
-    
+
     /**
-     * get phases of the current project; a project must be set be set before
-     * @return type
+     * Get phases of the current project; a project must be set be set before
+     * @return array
      */
-    public function getPhases(){
-        if (!$this->sProject && !$this->sPackage){
-            $this->_quit(__FUNCTION__ , "you need to set a project first. Use the setProject() method to do so.");
+    public function getPhases(): array
+    {
+        if (!$this->sProject && !$this->sPackage) {
+            $this->_quit(__FUNCTION__, "you need to set a project first. Use the setProject() method to do so.");
         }
-        $sWhere='1=1 '
-            . ($this->sProject ? "AND project='"  . $this->sProject . "' " : "")
-            . ($this->sPackage ? "AND package='"  . $this->sPackage . "' " : "")
-            ;
-        $sSql="select distinct(phase) from `values` WHERE $sWhere";
-        return $this->_makeSelectQuery($sSql,'phase');
+        $sWhere = '1=1 '
+            . ($this->sProject ? "AND project='" . $this->sProject . "' " : "")
+            . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "")
+        ;
+        $sSql = "select distinct(phase) from `values` WHERE $sWhere";
+        return $this->_makeSelectQuery($sSql, 'phase');
     }
-    
+
     /**
      * get places of the current project; a project and must be set be set before
      * @return type
@@ -259,254 +334,286 @@ class valuestore {
         return $this->_makeSelectQuery($sSql, 'place');
     }
      */
-    
+
     /**
      * get hosts that have installed a project
-     * @return type
+     * @return array
      */
-    public function getHosts(){
-        $sWhere='1=1 '
-            . ($this->sProject ? "AND project='"  . $this->sProject . "' " : "")
-            . ($this->sPackage ? "AND package='"  . $this->sPackage . "' " : "")
-            . ($this->sPhase   ? "AND phase='"  . $this->sPhase . "' "   : "")
+    public function getHosts(): array
+    {
+        $sWhere = '1=1 '
+            . ($this->sProject ? "AND project='" . $this->sProject . "' " : "")
+            . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "")
+            . ($this->sPhase ? "AND phase='" . $this->sPhase . "' " : "")
             . "AND place='deployed' "
             //. "AND host>'' "
-            ;
-        $sSql="select distinct(host) from `values` WHERE $sWhere";
+        ;
+        $sSql = "select distinct(host) from `values` WHERE $sWhere";
         return $this->_makeSelectQuery($sSql, 'host');
     }
 
     /**
-     * get entries of the current place (project, phase and place can be
+     * Get entries of the current place (project, phase and place can be
      * set before)
+     * 
      * @see setProject()
+     * 
      * @param string  $sVariable  variable for filtering column "variable"
      * @return array
      */
-    public function getVar($sVariable=''){
-        $sWhere='1=1 '
-            . ($this->sProject ? "AND project='"  . $this->sProject . "' " : "")
-            . ($this->sPackage ? "AND package='"  . $this->sPackage . "' " : "")
-            . ($this->sPhase   ? "AND phase='"    . $this->sPhase   . "' " : "")
-            . ($this->sPlace   ? "AND place='"    . $this->sPlace   . "' " : "")
-            . ($this->sHost    ? "AND host='"     . $this->sHost    . "' " : "")
-            . ($sVariable      ? "AND variable='" . $sVariable      . "' " : "")
-            ;
-        $sSql="select * from `values` WHERE $sWhere";
+    public function getVar($sVariable = ''): array
+    {
+        $sWhere = '1=1 '
+            . ($this->sProject ? "AND project='" . $this->sProject . "' " : "")
+            . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "")
+            . ($this->sPhase ? "AND phase='" . $this->sPhase . "' " : "")
+            . ($this->sPlace ? "AND place='" . $this->sPlace . "' " : "")
+            . ($this->sHost ? "AND host='" . $this->sHost . "' " : "")
+            . ($sVariable ? "AND variable='" . $sVariable . "' " : "")
+        ;
+        $sSql = "select * from `values` WHERE $sWhere";
         return $this->_makeSelectQuery($sSql);
     }
-    
+
     /**
-     * get versions of the current place (project, phase and place can be
+     * Get versions of the current place (project, phase and place can be
      * set before)
+     * 
      * @see setProject()
+     * 
      * @return array
      */
-    public function getVersion(){
-        $aData=$this->getVar('version');
-        
+    public function getVersion(): array
+    {
+        $aData = $this->getVar('version');
+
         // get json in subkey host -> data and store keys in host -> _data
-        if($aData && is_array($aData) && count($aData)){
-            foreach($aData as $iKey=>$aHostinfos){
-                $aJson=json_decode($aHostinfos['data'], 1);
-                $aData[$iKey]['_data']=$aJson;
+        if ($aData && is_array($aData) && count($aData)) {
+            foreach ($aData as $iKey => $aHostinfos) {
+                $aJson = json_decode($aHostinfos['data'], 1);
+                $aData[$iKey]['_data'] = $aJson;
             }
         }
         return $aData;
     }
-    
+
     /**
-     * return currebntly set project, phase, place and host
+     * Get currently set project, phase, place and host
      * @return array
      */
-    public function whereiam(){
-        return array(
-            'project'=>$this->sProject,
-            'package'=>$this->sPackage,
-            'phase'=>$this->sPhase,
-            'place'=>$this->sPlace,
-            'host'=>$this->sHost,
-        );
+    public function whereiam(): array
+    {
+        return [
+            'project' => $this->sProject,
+            'package' => $this->sPackage,
+            'phase' => $this->sPhase,
+            'place' => $this->sPlace,
+            'host' => $this->sHost,
+        ];
     }
+
     /**
-     * return currebntly set project, phase, place and host
-     * @return type
+     * Get all values in valuestore as array
+     * @return array
      */
-    public function dumpdb(){
-        $sSql="select * from `values`";
+    public function dumpdb(): array
+    {
+        $sSql = "select * from `values`";
         return $this->_makeSelectQuery($sSql);
     }
-    
-    
+
+
     // ----------------------------------------------------------------------
     // PUBLIC SETTER
     // ----------------------------------------------------------------------
-    
+
     /**
-     * init a "position" before getting or updating deleting a value
+     * Init a "position" before getting or updating deleting a value
      * 
      * @param string  $sProject  project id
      * @param string  $sPackage  package id
-     * @param string  $sPhase    phase (preview|stage|live)
-     * @param string  $sPlace    place (onhold|ready2install|deployed)
-     * @param string  $sHost     hostname (for place deployed)
-     * @return type string
+     * @param string  $sPhase    optional: phase (preview|stage|live)
+     * @param string  $sPlace    optional: place (onhold|ready2install|deployed)
+     * @param string  $sHost     optional: hostname (for place deployed)
+     * @return string
      */
-    public function setProject($sProject, $sPackage, $sPhase=false, $sPlace=false, $sHost=false){
-        $this->sProject=preg_replace('/[^a-z\-\_0-9]/', '' ,$sProject);
+    public function setProject(string $sProject, string $sPackage, string $sPhase = '', string $sPlace = '', $sHost = ''): string
+    {
+        $this->sProject = preg_replace('/[^a-z\-\_0-9]/', '', $sProject);
         $this->setPackage($sPackage, $sPhase, $sPlace, $sHost);
         return $this->sProject;
     }
 
     /**
-     * set a package (and more granular items)
+     * Set a package (and more granular items)
      * @see setProject()
      * 
      * @param string  $sPackage  package id
      * @param string  $sPhase    phase (preview|stage|live)
-     * @param string  $sPlace    place (onhold|ready2install|deployed)
-     * @param string  $sHost     hostname (for place deployed)
-     * @return type string
+     * @param string  $sPlace    optional: place (onhold|ready2install|deployed)
+     * @param string  $sHost     optional: hostname (for place deployed)
+     * @return string
      */
-    public function setPackage($sPackage, $sPhase, $sPlace=false, $sHost=false){
-        $this->sPackage=preg_replace('/[^a-z\-\_0-9]/', '' ,$sPackage);
+    public function setPackage(string $sPackage, string $sPhase, string $sPlace = '', string $sHost = ''): string
+    {
+        $this->sPackage = preg_replace('/[^a-z\-\_0-9]/', '', $sPackage);
         $this->setPhase($sPhase, $sPlace, $sHost);
         return $this->sPackage;
     }
-    
+
     /**
-     * set a phase (and more granular items)
+     * Set a phase (and more granular items)
      * @see setProject()
      * 
      * @param string  $sPhase    phase (preview|stage|live)
-     * @param string  $sPlace    place (onhold|ready2install|deployed)
-     * @param string  $sHost     hostname (for place deployed)
-     * @return type string
+     * @param string  $sPlace    optional: place (onhold|ready2install|deployed)
+     * @param string  $sHost     optional: hostname (for place deployed)
+     * @return string
      */
-    public function setPhase($sPhase, $sPlace=false, $sHost=false){
-        if (!$this->sProject && !$this->sPackage){
-            $this->_quit(__FUNCTION__ , "ERROR: you need to set a project. Use the setProject() method to do so.");
+    public function setPhase(string $sPhase, string $sPlace = '', string $sHost = ''): string
+    {
+        if (!$this->sProject && !$this->sPackage) {
+            $this->_quit(__FUNCTION__, "ERROR: you need to set a project. Use the setProject() method to do so.");
             return false;
         }
-        if ($sPhase && array_search($sPhase, $this->_allowedPhases)===false){
-            $this->_quit(__FUNCTION__ , "ERROR: you set a wrong phase [$sPhase]");
+        if ($sPhase && array_search($sPhase, $this->_allowedPhases) === false) {
+            $this->_quit(__FUNCTION__, "ERROR: you set a wrong phase [$sPhase]");
         }
-        $this->sPhase=$sPhase;
+        $this->sPhase = $sPhase;
         $this->setPlace($sPlace, $sHost);
         return $this->sPhase;
     }
 
     /**
-     * set a place (and host)
+     * Set a place (and optional a host)
+     * It aborts if project and package are not set
+     * 
      * @see setProject()
      * 
      * @param string  $sPlace    place (onhold|ready2install|deployed)
-     * @param string  $sHost     hostname (for place deployed)
-     * @return type string
+     * @param string  $sHost     optional: hostname (for place deployed)
+     * @return bool
      */
-    public function setPlace($sPlace, $sHost=false){
-        if ((!$this->sProject && !$this->sPackage)){
-            $this->_quit(__FUNCTION__ , "ERROR: you need to set a project and phase. Use the setProject() method to do so.");
+    public function setPlace($sPlace, string $sHost = ''): bool
+    {
+        if ((!$this->sProject && !$this->sPackage)) {
+            $this->_quit(__FUNCTION__, "ERROR: you need to set a project and phase. Use the setProject() method to do so.");
             return false;
         }
-        if ($sPlace && !$this->sPhase){
-            $this->_quit(__FUNCTION__ , "ERROR: you cannot set place [$sPlace] with leaving phase empty.");
+        if ($sPlace && !$this->sPhase) {
+            $this->_quit(__FUNCTION__, "ERROR: you cannot set place [$sPlace] with leaving phase empty.");
         }
-        if ($sPlace && array_search($sPlace, $this->_allowedPlaces)===false){
-            $this->_quit(__FUNCTION__ , "ERROR: you set a wrong place [$sPlace]");
+        if ($sPlace && array_search($sPlace, $this->_allowedPlaces) === false) {
+            $this->_quit(__FUNCTION__, "ERROR: you set a wrong place [$sPlace]");
         }
-        $this->sPlace=$sPlace;
+        $this->sPlace = $sPlace;
         $this->setHost($sHost);
-        return $this->sPlace;
+        return true;
     }
-    
+
     /**
-     * set a host for place "deployed"
+     * Set a host for place "deployed".
+     * It aborts if project and package are not set
+     * 
      * @see setProject()
      * 
      * @param string  $sHost     hostname
-     * @return type string
+     * @return bool
      */
-    public function setHost($sHost=false){
-        if ((!$this->sProject && !$this->sPackage)){
-            $this->_quit(__FUNCTION__ , "ERROR: you need to set a project, phase and place. Use the setProject() method to do so.");
+    public function setHost($sHost = ''): bool
+    {
+        if ((!$this->sProject && !$this->sPackage)) {
+            $this->_quit(__FUNCTION__, "ERROR: you need to set a project, phase and place. Use the setProject() method to do so.");
             return false;
         }
-        if($sHost && $this->sPlace!=='deployed'){
-            $this->_quit(__FUNCTION__ , "ERROR: a host can be set on place [deployed] only.");
+        if ($sHost && $this->sPlace !== 'deployed') {
+            $this->_quit(__FUNCTION__, "ERROR: a host can be set on place [deployed] only.");
         }
         /*
         if(!$sHost && $this->sPlace==='deployed'){
             $this->_quit(__FUNCTION__ , "ERROR: on place [deployed] a host MUST be set.");
         }
          */
-        $this->sHost=preg_replace('/[^a-z\.\-0-9]/', '', $sHost);
-        return $this->sHost;
+        $this->sHost = preg_replace('/[^a-z\.\-0-9]/', '', $sHost);
+        return true;
     }
-    
+
     // ----------------------------------------------------------------------
     // DATABASE FUNCTIONS
     // ----------------------------------------------------------------------
-    
+
     /**
-     * cleanup value store
+     * Cleanup value store: delete entries older a given ttl value in seconds
+     * Afterwards a "vacuum" command will be started.
+     * 
      * @param integer  $iTtl  optional: max age in seconds of items to keep; 0 to delete all; default is 1 day
      * @return boolean
      */
-    public function cleanup($iTtl=false){
-        if ($iTtl===false){
-            $iTtl=$this->_iTTL;
+    public function cleanup(int $iTtl = -1): bool
+    {
+        if ($iTtl === -1) {
+            $iTtl = $this->_iTTL;
         }
-        $sTtlDate=date("Y-m-d H:i:s", date("U") - (int)$iTtl );
-        $sSql="DELETE FROM `values` WHERE `time` < '$sTtlDate' "
-            . ($this->sProject ? "AND project='"  . $this->sProject . "' " : "")
-            . ($this->sPackage ? "AND package='"  . $this->sPackage . "' " : "")
-            . ($this->sPhase   ? "AND phase='"    . $this->sPhase   . "' " : "")
-            . ($this->sPlace   ? "AND place='"    . $this->sPlace   . "' " : "")
-            . ($this->sHost    ? "AND host='"     . $this->sHost    . "' " : "")
-            ;
+        $sTtlDate = date("Y-m-d H:i:s", date("U") - (int) $iTtl);
+        $sSql = "DELETE FROM `values` WHERE `time` < '$sTtlDate' "
+            . ($this->sProject ? "AND project='" . $this->sProject . "' " : "")
+            . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "")
+            . ($this->sPhase ? "AND phase='" . $this->sPhase . "' " : "")
+            . ($this->sPlace ? "AND place='" . $this->sPlace . "' " : "")
+            . ($this->sHost ? "AND host='" . $this->sHost . "' " : "")
+        ;
         // die("$iTtl ... $sSql ... ABORT");
         $this->_makeQuery($sSql);
         $this->_makeQuery("vacuum;");
         return true;
     }
-    
+
     /**
-     * delete values from value store. You need to call setProject() to set
+     * Delete values from value store. You need to call setProject() to set
      * project or package and optional phase, place, host. Then call this delete
      * method.
+     * It returns a PDO result object.
+     * It aborts if project and package are not set
+     * It returns false on database query error
+     * 
      * @param string $sVariable  optional: limit deletion to a given variable
-     * @return boolean
+     * @return boolean|object
      */
-    public function deleteValues($sVariable){
-        if ((!$this->sProject && !$this->sPackage) ){
-            $this->_quit(__FUNCTION__ , "ERROR: you need to set a project, phase and place. use the setProject() method to do so.");
+    public function deleteValues($sVariable): bool|object
+    {
+        if ((!$this->sProject && !$this->sPackage)) {
+            $this->_quit(__FUNCTION__, "ERROR: you need to set a project, phase and place. use the setProject() method to do so.");
         }
-        $sWhere='1=1 '
-            . ($this->sProject ? "AND project='"  . $this->sProject . "' " : "")
-            . ($this->sPackage ? "AND package='"  . $this->sPackage . "' " : "")
-            . ($this->sPhase   ? "AND phase='"    . $this->sPhase   . "' " : "")
-            . ($this->sPlace   ? "AND place='"    . $this->sPlace   . "' " : "")
-            . ($this->sHost    ? "AND host='"     . $this->sHost    . "' " : "")
-            . ($sVariable      ? "AND variable='" . $sVariable      . "' " : "")
-            ;
-        $sSql="DELETE FROM `values` WHERE $sWhere";
+        $sWhere = '1=1 '
+            . ($this->sProject ? "AND project='" . $this->sProject . "' " : "")
+            . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "")
+            . ($this->sPhase ? "AND phase='" . $this->sPhase . "' " : "")
+            . ($this->sPlace ? "AND place='" . $this->sPlace . "' " : "")
+            . ($this->sHost ? "AND host='" . $this->sHost . "' " : "")
+            . ($sVariable ? "AND variable='" . $sVariable . "' " : "")
+        ;
+        $sSql = "DELETE FROM `values` WHERE $sWhere";
         // echo $sSql; 
         // return true;
         return $this->_makeQuery($sSql);
     }
-    
+
     /**
-     * update a version
-     * @return boolean
+     * Update a version informatio in value store.
+     * It returns a PDO result object.
+     * It aborts if project and package are not set
+     * It returns false on database query error
+     * 
+     * @return boolean|object
      */
-    public function updateVar($sVarname,$sValue){
-        if ((!$this->sProject && !$this->sPackage) || !$this->sPhase || !$this->sPlace ){
-            $this->_quit(__FUNCTION__ , "ERROR: you need to set a project, phase and place. use the setProject() method to do so.");
+    public function updateVar($sVarname, $sValue): bool|object
+    {
+        if ((!$this->sProject && !$this->sPackage) || !$this->sPhase || !$this->sPlace) {
+            $this->_quit(__FUNCTION__, "ERROR: you need to set a project, phase and place. use the setProject() method to do so.");
         }
 
         $this->cleanup();
-        $sSql="
+        $sSql = "
         INSERT into `values` (`time`, `project`, `package`, `phase`, `place`, `host`, `variable`, `data` )
         VALUES (
             '" . date("Y-m-d H:i:s") . "',
@@ -520,20 +627,25 @@ class valuestore {
         );
         ";
         return $this->_makeQuery(
-                $sSql,
-                array(
-                    'variable'=>$sVarname,
-                    'value'=>$sValue,
-                )
+            $sSql,
+            [
+                'variable' => $sVarname,
+                'value' => $sValue,
+            ]
         );
     }
     /**
-     * update a version value
-     * @param type $sVersioninfos
-     * @return boolean
+     * update a version value. It returns the return value of updateVar().
+     * It returns a PDO result object.
+     * It aborts if project and package are not set
+     * It returns false on database query error
+     * 
+     * @param string $sVersioninfos  json of ci build data
+     * @return boolean|object
      */
-    public function updateVersion($sVersioninfos){
+    public function updateVersion($sVersioninfos): bool|object
+    {
         return $this->updateVar('version', $sVersioninfos);
     }
-    
+
 }
diff --git a/public_html/valuestore/index.php b/public_html/valuestore/index.php
index 9da4fb06d263612a47028375867df729eb698cbe..f554bdbfc4c0b6d274457348a838f65df87cb4f2 100644
--- a/public_html/valuestore/index.php
+++ b/public_html/valuestore/index.php
@@ -4,8 +4,6 @@
  * 
  * VALUE HANDLER API
  * 
- * Syntax des Aufrufs (als GET Schreibweise; ein POST einzelner Variablen geht ebenso):
- * 
  * update
  * http://dev.ci.iml.unibe.ch:8002/valuestore/?action=update&package=player&phase=stage&place=deployed&host=stage01.player&variable=version&value=false
  * 
@@ -16,38 +14,49 @@
  * 
  *      action=update (get-Funktionen muss ich später noch bauen)
  *      project=[Projekt-ID (=Name des Package, das installiert wird); bei Versionen leer!]
- *      package=[Package-name]
+ *      package=[Package-name] (Name des CI Projekts)
  *      phase=(preview|stage|live)
  *      host=[FQDN]
  *      variable=version (andere noch nicht unterstützt/ vorgesehen)
  *      value=[Versionsdaten]
  * 
  * 2017-04-07  hahn  first lines
+ *   ...
+ * 2024-09-04  Axel <axel.hahn@unibe.ch>  php8 only; added variable types; short array syntax
  */
 // ----------------------------------------------------------------------
 // functions
 // ----------------------------------------------------------------------
 
 /**
- * show an error message and quit with http status code 400 (Bad request)
+ * Show an error message and quit with http status code 400 (Bad request)
+ * 
  * @param string  $sMessage  message to show
- * @return boolean
+ * @return void
  */
-function quit($sMessage) {
+function quit(string $sMessage): void
+{
     $sProtocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
     header("$sProtocol 400: Bad request");
-    die("<h1>Bad request</h1>" . $sMessage);
-    return false;
+    die(
+        "<h1>Bad request</h1>
+        <p>$sMessage.</p>
+        <hr>
+        <button onclick=\"history.back();\">back</button>"
+    );
 }
 
 /**
- * get a request param from GET and POST scope (POST has priority) and
- * verify it with execution of a cleanup array
+ * Get a request param from GET and POST scope (POST has priority) and
+ * verify it with execution of a cleanup array.
+ * It returns false if thegiven key doesn't exist as parameter
+ * 
  * @param string  $sKey            key to search for in GET or POST
  * @param string  $sRegex4Cleanup  regex for filtering
- * @return type
+ * @return bool|int|string
  */
-function getParam($sKey, $sRegex4Cleanup = false) {
+function getParam(string $sKey, string $sRegex4Cleanup = ''): bool|int|string
+{
     $sValue = false;
     if (array_key_exists($sKey, $_GET)) {
         $sValue = $_GET[$sKey];
@@ -66,29 +75,42 @@ function getParam($sKey, $sRegex4Cleanup = false) {
     return $sReturn;
 }
 
-// ----------------------------------------------------------------------
-// check required params
-// ----------------------------------------------------------------------
-/*
-if (!$_GET || !count($_GET)) {
-    quit("no parameter was found.");
-}
+/**
+ * Throw an error if a given GET parameter has a given value.
+ * 
+ * The purpose of this function is to prevent certain actions
+ * (like deleting a value) from being triggered by an URL.
+ * 
+ * @param string $sVar   name of the GET parameter to check
+ * @param string $sValue value of the GET parameter to check
+ * @return void
  */
-
+function disallowGet(string $sVar, string $sValue): void
+{
+    if (isset($_GET[$sVar]) && $_GET[$sVar] == $sValue) {
+        quit("ERROR: $sVar = $sValue is not allowed with GET.");
+    }
+}
 
 // ----------------------------------------------------------------------
 // get vars
 // ----------------------------------------------------------------------
 
 $sAction = getParam('action', '/[^a-z]/');
-if ($sAction !== "cleanup" 
-        && $sAction !== "delete" 
-        && $sAction !== "get" 
-        && $sAction !== "show"
-        && $sAction !== "update" 
-        ) {
+if (
+    $sAction !== "cleanup"
+    && $sAction !== "delete"
+    && $sAction !== "get"
+    && $sAction !== "show"
+    && $sAction !== "update"
+) {
     quit("action is unknown or not implemented yet.");
 }
+disallowGet('action', 'cleanup');
+disallowGet('action', 'delete');
+
+// used by Anasible to update the value ... I disable it for now to fix it in the near future
+// disallowGet('action', 'update');
 
 $sProject = getParam('project', '/[^a-z\-\_0-9]/');
 $sPackage = getParam('package', '/[^a-z\-\_0-9]/');
@@ -127,13 +149,12 @@ switch ($sAction) {
         }
         break;
 
-        break;
     case 'get':
         $oVersion->setProject($sProject, $sPackage, $sPhase, $sPlace, $sHost);
         header('Content-Type: application/json');
         echo json_encode($oVersion->getVersion());
+        return true;
 
-        break;
     case 'update':
         $oVersion->setProject($sProject, $sPackage, $sPhase, $sPlace, $sHost);
         if (!$sVarname) {
@@ -143,7 +164,7 @@ switch ($sAction) {
             // case 'other-varname':
             case 'version':
                 if ($oVersion->updateVar($sVarname, $sValue)) {
-                    echo "OK: $sVarname was updated.".PHP_EOL."new value: $sValue".PHP_EOL;
+                    echo "OK: $sVarname was updated." . PHP_EOL . "new value: $sValue" . PHP_EOL;
                 } else {
                     echo "ERROR: update for $sVarname failed.";
                 }
@@ -156,19 +177,18 @@ switch ($sAction) {
         break;
     case 'show':
         $oVersion->setProject($sProject, $sPackage, $sPhase, $sPlace, $sHost);
-        
-        echo '<h1>package ['.$sPackage.'] on '.$sHost.'</h1>'
-                . 'Host: '.$sHost.'<br>'
-                . 'package: '.$sPackage.'<br>'
-                . 'phase: '.$sPhase.'<br>'
-                . 'place: '.$sPlace.'<br>'
-                . '<br>'
-                . '<button onclick="history.back();">back</button><br>'
-                . '<hr>'
-                . '<pre>'.json_encode($oVersion->getVersion(), JSON_PRETTY_PRINT).'</pre>'
-                . '<hr>'
-                . '<button onclick="history.back();">back</button>'
-                . '';
+
+        echo '<h1>package [' . $sPackage . '] on ' . $sHost . '</h1>'
+            . 'Host: ' . $sHost . '<br>'
+            . 'package: ' . $sPackage . '<br>'
+            . 'phase: ' . $sPhase . '<br>'
+            . 'place: ' . $sPlace . '<br>'
+            . 'varaible: ' . $sVarname . '<br>'
+            . '<br>'
+            . '<button onclick="history.back();">back</button><br>'
+            . '<hr>'
+            . '<pre>' . json_encode($oVersion->getVersion(), JSON_PRETTY_PRINT) . '</pre>'
+            . '';
 
         break;
 
@@ -177,3 +197,5 @@ switch ($sAction) {
         quit("ERROR: the action is not supported.");
         break;
 }
+
+echo "<hr><button onclick=\"history.back();\">back</button>";
\ No newline at end of file
diff --git a/public_html/vendor/shooker/shooker.php b/public_html/vendor/shooker/shooker.php
index b042bb7956478e32379fdc7e94cd9772da314b1e..8956f64a3f8092e7af7e400507c19aa8747b6d6f 100644
--- a/public_html/vendor/shooker/shooker.php
+++ b/public_html/vendor/shooker/shooker.php
@@ -160,5 +160,3 @@ class ShookerTrigger {
     array_push($this->actions, $fxn);
   }
 }
-
-?>
\ No newline at end of file
diff --git a/public_html/versions/classes/versions.class.php b/public_html/versions/classes/versions.class.php
deleted file mode 100644
index c3da08da4423a581cffa778cdd080d00c1f85587..0000000000000000000000000000000000000000
--- a/public_html/versions/classes/versions.class.php
+++ /dev/null
@@ -1,311 +0,0 @@
-<?php
-/**
- * version data handler for the deployment tool
- *
- * @author hahn
- */
-class versions {
-    //put your code here
-
-    public $sProject = false;
-    public $sPhase = false;
-    public $sPlace = false;
-    public $sVariable = false;
-    public $sData = false;
-
-    /**
-     * filename of sqlite database file
-     * @var type 
-     */
-    private $_dbfile = false;
-    
-    /**
-     * create statement for the database
-     * @var type 
-     */
-    private $_sCreate = '
-        CREATE TABLE "versions" (
-          `id` INTEGER PRIMARY KEY  AUTOINCREMENT  NOT NULL  UNIQUE ,
-          `time` DATETIME,
-          `project` TEXT,
-          `phase` TEXT,
-          `place` TEXT,
-          `host` TEXT,
-          `variable` TEXT,
-          `data` TEXT,
-          UNIQUE (project, phase, place, host, variable) ON CONFLICT REPLACE
-        );'
-    ;
-    
-    // ----------------------------------------------------------------------
-    // CONSTRUCTOR
-    // ----------------------------------------------------------------------
-    
-    /**
-     * constructor ... no params
-     */
-    public function __construct(){
-        
-        // cache dir is hardcoded to versions directory :-/
-        $this->_dbfile = __DIR__ . '/../data/versioncache.db';
-        
-        if (!file_exists($this->_dbfile)) {
-            $this->_createDb();
-        }
-    }
-    
-    // ----------------------------------------------------------------------
-    // PRIVATE 
-    // ----------------------------------------------------------------------
-
-    /**
-     * create sqlite database - called in constructor if the file does not exist
-     */
-    private function _createDb() {
-        if (file_exists($this->_dbfile)) {
-            echo "removing existing file $this->_dbfile ...<br>\n";
-            unlink($this->_dbfile);
-        }
-        echo "create database as file $this->_dbfile ...<br>\n";
-        $this->_makeQuery($this->_sCreate);
-        if (!file_exists($this->_dbfile)) {
-            die("ERROR: unable to create sqlite database " . $this->_dbfile);
-        }
-        return true;
-    }
-
-    /**
-     * execute a sql statement
-     * @param string $sSql   sql statement
-     * @param array  $aVars  array with values (uses PDO::prepare(); $sSql must contain placeholders :key)
-     * @return database object
-     */
-    private function _makeQuery($sSql, $aVars=false) {
-        // $this->_log(__FUNCTION__."($sSql)");
-        // echo "DEBUG: executing SQL<pre>$sSql</pre>";
-        $oDb = new PDO("sqlite:" . $this->_dbfile);
-        if ($aVars && is_array($aVars)){
-            $sth = $oDb->prepare($sSql);
-            $result = $sth->execute($aVars);
-        } else {
-            $result = $oDb->query($sSql);
-        }
-        return $result;
-    }
-    
-    /**
-     * execute a sql statement
-     * @param string $sSql sql statement
-     * @return database object
-     */
-    private function _makeSelectQuery($sSql, $aKey=false) {
-        // $this->_log(__FUNCTION__."($sSql)");
-        // echo "DEBUG: executing select SQL<pre>$sSql</pre>";
-        $oDb = new PDO("sqlite:" . $this->_dbfile);
-        $oStatement = $oDb->prepare($sSql);
-        $oStatement->execute();
-        $aReturn=array();
-        while ($row = $oStatement->fetch(PDO::FETCH_ASSOC)) {
-          if ($aKey && array_key_exists($aKey, $row)){
-            $aReturn[] = $row[$aKey];
-          } else {
-            $aReturn[] = $row;
-          }
-        }        
-        return $aReturn;
-    }
-
-
-    // ----------------------------------------------------------------------
-    // PUBLIC GETTER
-    // ----------------------------------------------------------------------
-    
-    
-    /**
-     * get list of current projects
-     * @return type
-     */
-    public function getProjects(){
-        $sSql="select distinct(project) from `versions`";
-        return $this->_makeSelectQuery($sSql, 'project');
-    }
-    
-    /**
-     * get phases of the current project; a project must be set be set before
-     * @return type
-     */
-    public function getPhases(){
-        if (!$this->sProject){
-            die("ERROR: you need to set a project first. Use the setProject() method to do so.");
-        }
-        $sSql="select distinct(phase) from `versions`
-            WHERE
-                project='" . $this->sProject . "'
-            "
-            ;
-        return $this->_makeSelectQuery($sSql,'phase');
-    }
-    
-    /**
-     * get places of the current project; a project and must be set be set before
-     * @return type
-     */
-    public function getPlaces(){
-        if (!$this->sProject || !$this->sPhase){
-            die("ERROR: you need to set a project, and phase first. Use the setProject() method to do so.");
-        }
-        $sSql="select distinct(place) from `versions`
-            WHERE
-                project='" . $this->sProject . "'
-                AND phase='" . $this->sPhase . "'
-                ";
-        return $this->_makeSelectQuery($sSql, 'place');
-    }
-    
-    /**
-     * get hosts that have installed a project
-     * @return type
-     */
-    public function getHosts(){
-        if (!$this->sProject || !$this->sPhase){
-            die("ERROR: you need to set a project, and phase first. Use the setProject() method to do so.");
-        }
-        $sSql="select distinct(host) from `versions`
-            WHERE
-                project='" . $this->sProject . "'
-                AND phase='" . $this->sPhase . "'
-                AND place='deployed'
-                ";
-        return $this->_makeSelectQuery($sSql, 'host');
-    }
-    
-    /**
-     * get versions of the current place (project, phase and place must be
-     * set before)
-     * @see setProject()
-     * @return type
-     */
-    public function getVersion(){
-        if (!$this->sProject || !$this->sPhase || !$this->sPlace ){
-            die("ERROR: you need to set a project, phase and place first. Use the setProject() method to do so.");
-        }
-        $sSql="select data from `versions`
-            WHERE
-                project='" . $this->sProject . "'
-                AND phase='" . $this->sPhase . "'
-                AND place='" . $this->sPlace . "'
-                AND host='" . $this->sHost . "'
-                AND variable='version'
-                ";
-        return $this->_makeSelectQuery($sSql, 'data');
-    }
-    
-    /**
-     * return currebntly set project, phase, place and host
-     * @return type
-     */
-    public function whereiam(){
-        return array(
-            'project'=>$this->sProject,
-            'phase'=>$this->sPhase,
-            'place'=>$this->sPlace,
-            'host'=>$this->sHost,
-        );
-    }
-    /**
-     * return currebntly set project, phase, place and host
-     * @return type
-     */
-    public function dumpdb(){
-        $sSql="select * from `versions`";
-        return $this->_makeSelectQuery($sSql);
-    }
-    
-    
-    // ----------------------------------------------------------------------
-    // PUBLIC SETTER
-    // ----------------------------------------------------------------------
-    
-    public function setProject($sProject, $sPhase=false, $sPlace=false, $sHost=false){
-        $this->sProject=$sProject;
-        $this->setPhase($sPhase, $sPlace, $sHost);
-        return $this->sProject;
-    }
-    
-    public function setPhase($sPhase, $sPlace=false, $sHost=false){
-        if (!$this->sProject){
-            die("ERROR: you need to set a project. Use the setProject() method to do so.");
-            return false;
-        }
-        $this->sPhase=$sPhase;
-        $this->setPlace($sPlace, $sHost);
-        return $this->sPhase;
-    }
-
-    public function setPlace($sPlace, $sHost=false){
-        if (!$this->sProject || !$this->sPhase){
-            die("ERROR: you need to set a project and phase. Use the setProject() method to do so.");
-            return false;
-        }
-        if($sPlace!=='onhold' && $sPlace!=='ready2install' && $sPlace!=='deployed'){
-            die("ERROR: you set a wrong place [$sPlace]");
-        }
-        $this->sPlace=$sPlace;
-        $this->setHost($sHost);
-        return $this->sPlace;
-    }
-    
-    public function setHost($sHost=false){
-        if (!$this->sProject || !$this->sPhase || !$this->sPlace){
-            die("ERROR: you need to set a project, phase and place. Use the setProject() method to do so.");
-            return false;
-        }
-        if($sHost && $this->sPlace!=='deployed'){
-            die("ERROR: a host can be set on place [deployed] only.");
-        }
-        if(!$sHost && $this->sPlace==='deployed'){
-            die("ERROR: on place [deployed] a host MUST be set.");
-        }
-        $this->sHost=$sHost;
-        return $this->sHost;
-    }
-    
-    /**
-     * update a version
-     * @return boolean
-     */
-    public function updateVar($sVarname,$sValue){
-        if (!$this->sProject || !$this->sPhase || !$this->sPlace ){
-            die("ERROR: you need to set a project, phase and place. use the setProject() method to do so.");
-        }
-        $sSql="
-        INSERT into `versions` 
-        (`time`, `project`, `phase`, `place`, `host`, `variable`, `data` )
-        VALUES
-        (
-            '" . date("Y-m-d H:i:s") . "',
-            '" . $this->sProject . "',
-            '" . $this->sPhase . "',
-            '" . $this->sPlace . "',
-            '" . $this->sHost . "',
-            :variable,
-            :value
-        );
-        ";
-        return $this->_makeQuery(
-                $sSql,
-                array(
-                    'variable'=>$sVarname,
-                    'value'=>$sValue,
-                )
-        );
-    }
-    /**
-     * update a version
-     * @return boolean
-     */
-    public function updateVersion($sVersioninfos){
-        return $this->updateVar('version', $sVersioninfos);
-    }
-    
-}
diff --git a/public_html/versions/data/.htkeep b/public_html/versions/data/.htkeep
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/public_html/versions/index.php b/public_html/versions/index.php
deleted file mode 100644
index 8fbb34621e34ff0726834e92b03f430b06a15d54..0000000000000000000000000000000000000000
--- a/public_html/versions/index.php
+++ /dev/null
@@ -1,140 +0,0 @@
-<?php
-
-/**
- * 
- * VERSION HANDLER API
- * 
- * Syntax des Aufrufs (als GET Schreibweise; ein POST soll (später) auch gehen):
- * http://dev.ci.iml.unibe.ch:8002/versions/?action=update&project=ci&phase=preview&host=ci.preview.iml.unibe.ch&variable=version&value=007
- * 
- * Parameter:
- * 
- *      action=update (get-Funktionen muss ich später noch bauen)
- *      project=[Projekt-ID (=Name des Package, das installiert wird)]
- *      phase=(preview|stage|live)
- *      host=[FQDN]
- *      variable=version (andere noch nicht unterstützt/ vorgesehen)
- *      value=[Versionsdaten]
- * 
- * 2017-04-07  hahn  first lines
- */
-
-
-// ----------------------------------------------------------------------
-// functions
-// ----------------------------------------------------------------------
-
-/**
- * show an error message and quit with http status code 400 (Bad request)
- * @param string  $sMessage  message to show
- * @return boolean
- */
-function quit($sMessage) {
-    $sProtocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
-    header("$sProtocol 400: Bad request");
-    die("<h1>Bad request</h1>" . $sMessage);
-    return false;
-}
-
-/**
- * get a request param from GET and POST scope (POST has priority) and
- * verify it with execution of a cleanup array
- * @param string  $sKey            key to search for in GET or POST
- * @param string  $sRegex4Cleanup  regex for filtering
- * @return type
- */
-function getParam($sKey, $sRegex4Cleanup = false) {
-    $sValue=false;
-    if (array_key_exists($sKey, $_GET)) {
-        $sValue=$_GET[$sKey];
-    }
-    if (array_key_exists($sKey, $_POST)) {
-        $sValue=$_POST[$sKey];
-    }
-    if(!$sValue){
-        return false;
-    }
-    $sReturn = $sRegex4Cleanup ? preg_replace($sRegex4Cleanup, '', $sValue) : $sValue;
-
-    if ($sReturn !== $sValue) {
-        quit("ERROR: the parameter value in [$sKey = ...] has a wrong format.");
-    }
-    return $sReturn;
-}
-
-// ----------------------------------------------------------------------
-// check required params
-// ----------------------------------------------------------------------
-if (!$_GET || !count($_GET)) {
-    quit("no parameter was found.");
-}
-
-
-foreach (array("action", "project") as $sKey) {
-    if (!array_key_exists($sKey, $_GET)) {
-        quit("value required: $sKey=");
-    }
-}
-
-// ----------------------------------------------------------------------
-// get vars
-// ----------------------------------------------------------------------
-
-
-$sAction = getParam('action', '/[^a-z]/');
-if ($sAction !== "get" && $sAction !== "update") {
-    quit("action is unknown or not implemented yet.");
-}
-
-$sProject = getParam('project', '/[^a-z\-0-9]/');
-$sPhase = getParam('phase', '/[^a-z]/');
-$sPlace = getParam('place', '/[^a-z]/');
-$sHost = getParam('host', '/[^a-z\.\-0-9]/');
-
-if ($sHost && !$sPlace) {
-    $sPlace = 'deployed';
-}
-
-$sVarname = getParam('variable', '/[^a-z]/');
-$sValue = getParam('value', '');
-
-
-// ----------------------------------------------------------------------
-// init class
-// ----------------------------------------------------------------------
-
-require './classes/versions.class.php';
-
-$oVersion = new versions();
-$oVersion->setProject($sProject, $sPhase, $sPlace, $sHost);
-
-switch ($sAction) {
-    case 'get':
-        print_r($oVersion->getVersion());
-
-        break;
-    case 'update':
-        if (!$sVarname) {
-            quit("ERROR: the update action requires a variable and a value.");
-        }
-        switch ($sVarname) {
-            // case 'other-varname':
-            case 'version':
-                if ($oVersion->updateVar($sVarname, $sValue)){
-                    echo "OK: $sVarname was updated.";
-                } else {
-                    echo "ERROR: update for $sVarname failed.";
-                }
-                break;
-
-            default:
-                quit("ERROR: update of variable [$sVarname] is not supported.");
-                break;
-        }
-        break;
-
-
-    default:
-        quit("ERROR: the action is not supported.");
-        break;
-}