diff --git a/public_html/appmonitor/check-appmonitor-server.php b/public_html/appmonitor/check-appmonitor-server.php
new file mode 100644
index 0000000000000000000000000000000000000000..35bd349c19fbc23d7ad74f95f86ca27976a372a1
--- /dev/null
+++ b/public_html/appmonitor/check-appmonitor-server.php
@@ -0,0 +1,45 @@
+<?php
+/* ______________________________________________________________________
+ * 
+ * A P P M O N I T O R  ::  CLIENT - CHECK
+ * ______________________________________________________________________
+ * 
+ * This is the check file for the appmonitor server installation
+ * Have a look to the docs/client-php.md and index.sample.php
+ * to write your own checks
+ * 
+ * @author: Axel Hahn
+ * ----------------------------------------------------------------------
+ * 2019-04-29  aded check for ssl cert; removed a check
+ * 2019-05-17  aded check http to config- and tmp dir
+ * 2021-11-nn  removed all checks ... created as single files
+ * 2022-03-28  put checks into plugins/apps/
+ */
+
+$sApproot = str_replace('\\', '/', dirname(__DIR__));
+
+require_once($sApproot.'/client/classes/appmonitor-client.class.php');
+
+// require_once('classes/client_all_in_one.php');
+$oMonitor = new appmonitor();
+$oMonitor->setWebsite('Appmonitor server');
+
+// how often the server should ask for updates
+$oMonitor->setTTL(300);
+$oMonitor->addTag('monitoring');
+
+
+// a general include ... the idea is to a file with the same actions on all
+// installations and hosts that can be deployed by a software delivery service 
+// (Puppet, Ansible, ...)
+@include 'general_include.php';
+
+// include default checks for an application
+@require 'plugins/apps/iml-appmonitor-server.php';
+
+// ----------------------------------------------------------------------
+
+$oMonitor->setResult();
+$oMonitor->render();
+
+// ----------------------------------------------------------------------
diff --git a/public_html/appmonitor/classes/appmonitor-client.class.php b/public_html/appmonitor/classes/appmonitor-client.class.php
index ec21adcff914a8cfe77327899dff1edfab3c4311..711c8e457f2bdd56708d42c8d2a611d6d8a1cf4f 100644
--- a/public_html/appmonitor/classes/appmonitor-client.class.php
+++ b/public_html/appmonitor/classes/appmonitor-client.class.php
@@ -32,15 +32,16 @@ if (!class_exists('appmonitorcheck')){
  * --------------------------------------------------------------------------------<br>
  * <br>
  * --- HISTORY:<br>
- * 2014-10-24  0.5   axel.hahn@iml.unibe.ch<br>
- * 2014-11-21  0.6   axel.hahn@iml.unibe.ch  removed meta::ts <br>
- * 2018-08-23  0.50  axel.hahn@iml.unibe.ch  show version<br>
- * 2018-08-24  0.51  axel.hahn@iml.unibe.ch  method to show local status page<br>
- * 2018-08-27  0.52  axel.hahn@iml.unibe.ch  add pdo connect (starting with mysql)<br>
- * 2018-11-05  0.58  axel.hahn@iml.unibe.ch  additional flag in http check to show content<br>
- * 2019-05-31  0.87  axel.hahn@iml.unibe.ch  add timeout as param in connective checks (http, tcp, databases)<br>
+ * 2014-10-24  0.5    axel.hahn@iml.unibe.ch<br>
+ * 2014-11-21  0.6    axel.hahn@iml.unibe.ch  removed meta::ts <br>
+ * 2018-08-23  0.50   axel.hahn@iml.unibe.ch  show version<br>
+ * 2018-08-24  0.51   axel.hahn@iml.unibe.ch  method to show local status page<br>
+ * 2018-08-27  0.52   axel.hahn@iml.unibe.ch  add pdo connect (starting with mysql)<br>
+ * 2018-11-05  0.58   axel.hahn@iml.unibe.ch  additional flag in http check to show content<br>
+ * 2019-05-31  0.87   axel.hahn@iml.unibe.ch  add timeout as param in connective checks (http, tcp, databases)<br>
+ * 2020-05-03  0.110  axel.hahn@iml.unibe.ch  update renderHtmloutput<br>
  * --------------------------------------------------------------------------------<br>
- * @version 0.99
+ * @version 0.111
  * @author Axel Hahn
  * @link TODO
  * @license GPL
@@ -54,7 +55,7 @@ class appmonitor {
      * value is in seconds
      * @var int
      */
-    protected $_sVersion = 'php-client-v0.109';
+    protected $_sVersion = 'php-client-v0.111';
 
     /**
      * config: default ttl for server before requesting the client check again
@@ -481,34 +482,44 @@ class appmonitor {
                 . '</div>'
                 . 'Host: '    . (isset($aData['meta']['host'])    ? '<span class="string">' . $aData['meta']['host']   .'</span>'    : '?').'<br>'
                 . 'Website: ' . (isset($aData['meta']['website']) ? '<span class="string">' . $aData['meta']['website'].'</span>'  : '?').'<br>'
-                // . 'Status: '  . (isset($aData['meta']['result'])  ? '<span class="result'.$aData['meta']['result'].'">'. $aMsg[$aData['meta']['result']].'</span>'  : '?').'<br>'
                 . 'Execution time: '    . (isset($aData['meta']['time'])    ? '<span class="float">'  . $aData['meta']['time']  .'</span>'  : '?').'<br>'
+                . 'Client: '    . (isset($aData['meta']['version'])    ? '<span class="float">'  . $aData['meta']['version']  .'</span>'  : '?').'<br>'
 
                 .'<h2>Checks</h2>'
                 ;
         if (isset($aData['checks'][0]) && count($aData['checks'])){
             foreach($aData['checks'] as $aCheck){
-               $sOut.= '<span class="result'.$aCheck['result'].'"> <strong>'.$aCheck['name'].'</strong></span> <br>'
-                   . $aCheck['description'].'<br>'
-                   // . '<span class="result'.$aCheck['result'].'">'.$aCheck['value'].'</span><br>'
-                   . $aCheck['value'].'<br>'
-                   . 'Execution time: ' . $aCheck['time'].'<br>'
-                   . 'Status: '  . $aMsg[$aCheck['result']].'<br>'
-                   . '<br>'
+               $sOut.= ''
+               . '<span class="result'.$aCheck['result'].'"> <strong>'.$aCheck['name'].'</strong></span> <br>'
+               . '<div class="check">'
+                    . '<div class="description">'
+                        . $aCheck['description'].'<br>'
+                        . $aCheck['value'].'<br>'
+                    . '</div>'
+                    . 'Execution time: ' . (isset($aCheck['time']) ? $aCheck['time'] : ' - ').'<br>'
+                    . 'Group: '  . (isset($aCheck['group']) ? $aCheck['group'] : '-').'<br>'
+                    . 'parent: ' . (isset($aCheck['parent']) ? $aCheck['parent']: '-').'<br>'
+                    . 'Status: ' . $aMsg[$aCheck['result']].'<br>'
+                   . '</div>'
                    ;
             }
         }
-        $sOut.= '<hr>List of farbcodes: ';
+        $sOut.= '<h2>List of farbcodes</h2>';
         foreach ($aMsg as $i=>$sText){
             $sOut.= '<span class="result'.$i.'">'. $sText.'</span> ';
         }
+        $sOut.='<h2>Raw result data</h2><pre>'.json_encode($aData, JSON_PRETTY_PRINT).'</pre>';
         $sOut = '<!DOCTYPE html><html><head>'
                 . '<style>'
                 . 'body{background:#fff; color:#444; font-family: verdana,arial; margin: 3em;}'
-                . '.result0{background:#aca; border-left: 1em solid #080; padding: 0 0.5em; }'
-                . '.result1{background:#ccc; border-left: 1em solid #aaa; padding: 0 0.5em; }'
-                . '.result2{background:#fc9; border-left: 1em solid #860; padding: 0 0.5em; }'
-                . '.result3{background:#f88; border-left: 1em solid #f00; padding: 0 0.5em; }'
+                . 'h1{color:#346;}'
+                . 'h2{color:#569; margin-top: 1.5em;}'
+                . '.check{border: 1px solid; padding: 0.4em; margin-bottom: 2em;}'
+                . '.description{font-style: italic; padding: 0.4em 1em;}'
+                . '.result0{background:#aca; border-left: 1em solid #080; padding: 0.5em; }'
+                . '.result1{background:#ccc; border-left: 1em solid #aaa; padding: 0.5em; }'
+                . '.result2{background:#fc9; border-left: 1em solid #860; padding: 0.5em; }'
+                . '.result3{background:#f88; border-left: 1em solid #f00; padding: 0.5em; }'
                 . '</style>'
                 . '<title>' . __CLASS__ . '</title>'
                 . '</head><body>'
diff --git a/public_html/appmonitor/example.json b/public_html/appmonitor/example.json
deleted file mode 100644
index b856bf639168b05d9db88cbf7d02e2183a19ae4c..0000000000000000000000000000000000000000
--- a/public_html/appmonitor/example.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
-    "meta": {
-        "host": "my-computer", 
-        "website": "localhost", 
-        "ttl": 300, 
-        "result": 1
-    },
-    "checks": [
-        {
-            "name": "simple ok", 
-            "description": "Very simple test", 
-            "result": 0, 
-            "value": "The appmonitor client is reachable."
-        },
-        {
-            "name": "simple unknown", 
-            "description": "a dummy test with unknown status", 
-            "result": 1, 
-            "value": "any message"
-        },
-        {
-            "name": "simple warning", 
-            "description": "a dummy warning", 
-            "result": 2, 
-            "value": "warning message"
-        },
-        {
-            "name": "dummy error", 
-            "description": "a dummy error test", 
-            "result": 3, 
-            "value": "ERROR: the error message was not found"
-        } 
-    ] 
-}
\ No newline at end of file
diff --git a/public_html/appmonitor/general_include.sample.php b/public_html/appmonitor/general_include.sample.php
deleted file mode 100644
index f5ebae3bc79239323bd852a7089391aca87c881f..0000000000000000000000000000000000000000
--- a/public_html/appmonitor/general_include.sample.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-/* ______________________________________________________________________
- * 
- * A P P M O N I T O R  ::  CLIENT - CHECK  ::  GENERAL INCLUDE
- * ______________________________________________________________________
- * 
- * The idea behind  is to a file with the same actions on all your 
- * installations and hosts that can be deployed by a software delivery service 
- * (Puppet, Ansible, ...)
- * 
- * Instruction:
- * (1) copy sample file to general_include.php and enable wished features below
- * (2) in your checks enable it by
- *     @include 'general_include.php';
- *     see index.sample.php too
- * 
- * @author: Axel Hahn
- * ----------------------------------------------------------------------
- * 2018-06-30  v0.1
- */
-
-// ----------------------------------------------------------------------
-// SECURITY STUFF ... protect access to monitoring data
-// ----------------------------------------------------------------------
-
-// --- check an IP range of allowed clients
-/*
-$oMonitor->checkIp(array(
-    '127.0.0.1',
-    '::1',
-    '192.168.',
-));
- */
-
-// --- check a token
-// an incoming request must have the param ?token=123
-// $oMonitor->checkToken('token', '123');
-
-
-// ----------------------------------------------------------------------
-// NOTIFICATION
-// ----------------------------------------------------------------------
-
-// $oMonitor->addEmail('sysadmin@example.com');
-// $oMonitor->addSlackWebhook(array("mywebhook"=> "https://hooks.slack.com/services/(...)"));
-
diff --git a/public_html/appmonitor/git_update_appmonitor.sh b/public_html/appmonitor/git_update_appmonitor.sh
new file mode 100644
index 0000000000000000000000000000000000000000..fdc069e9af8ee466adb82c9be0e220efa504cd5d
--- /dev/null
+++ b/public_html/appmonitor/git_update_appmonitor.sh
@@ -0,0 +1,175 @@
+#!/bin/bash
+# ======================================================================
+#
+# UPDATE APPMONITOR CLIENT
+#
+# requires git, rsync
+#
+# ----------------------------------------------------------------------
+# 2022-04-11  <axel.hahn@iml.unibe.ch>  first lines
+# 2022-04-12  <axel.hahn@iml.unibe.ch>  add help; exclude unneeded files
+# 2022-05-03  <axel.hahn@iml.unibe.ch>  create general_include.php
+# ======================================================================
+
+# ----------------------------------------------------------------------
+# CONFIG
+# ----------------------------------------------------------------------
+
+readonly git_repo_url="https://github.com/iml-it/appmonitor.git"
+readonly line="____________________________________________________________"
+readonly version="0.3"
+
+git_target=/tmp/git_data__appmonitor
+client_from="${git_target}/public_html/client"
+client_to="."
+
+cd $( dirname "$0" ) || exit 1
+
+# ----------------------------------------------------------------------
+# FUNCTIONS
+# ----------------------------------------------------------------------
+
+# Create a missing file from sample file
+#
+# global $client_from  source dir with git repo data
+# global $client_to    target dir
+#
+# param  string  source file (containing .sample); relative to $client_from
+function _fileupdate(){
+    local _myfile=$1
+    local _newfile=${_myfile//.sample/}
+    echo -n "Update $client_from/$_myfile --> $client_to/$_newfile ... "
+    
+    if [ ! -f "$client_to/$_newfile" ]; then
+        echo -n "copy ... "
+        cp "$client_from/$_myfile" "$client_to/$_newfile" || exit 2
+        echo "OK"
+    else
+        echo "already exists - SKIP "
+    fi
+
+}
+
+# get data from a repo with git clone or git pull
+# param string  url of public .git repo
+# param string  local directory where to clone it
+function _gitUpdate(){
+    local _url=$1
+    local _dirgit=$2
+    local _rc=0
+    if [ -d "$_dirgit" ]; then
+        cd "$_dirgit" || exit 1
+        _logBefore=$( git log -1 );
+        echo "Update local data from repo... with git pull "
+        git pull
+        _logAfter=$( git log -1 ); 
+        if [ "$_logBefore" != "$_logAfter" ]; then
+            _rc=1
+        fi
+        cd - >/dev/null || exit 1
+    else
+        echo "Cloning..."
+        git clone "$_url" "$_dirgit"
+        _rc=$?
+    fi
+    return $_rc
+}
+
+
+# ----------------------------------------------------------------------
+# MAIN
+# ----------------------------------------------------------------------
+
+cat <<ENDOFHEADER
+
+          +-----------------------------------+
+          |                                   |
+          |  INSTALLER  |                     |
+          |      +      |  Appmonitor client  |
+          |   UPDATER   |                     |
+          |                                   |
+          +--------------------------- v$version --+
+
+ENDOFHEADER
+
+case "$1" in
+    -h|--help)
+        cat <<ENDOFHELP
+
+    This is a helper script to get the files of the IML Appmonitor
+    client part only.
+
+    This script clones and updates the repository in the /tmp 
+    directory and syncs the client files of it to a given directory.
+
+    In the first run it works like an installer.
+    On additional runs it updates the files.
+
+    USAGE:
+
+    $0 [target path]
+
+        default target is [.] (current directory)
+
+    $0 -h|--help
+
+        Show this help.
+
+ENDOFHELP
+        exit 0
+        ;;
+    *)
+        if test -n "$1" 
+            then
+            if  ! test -d "$1"
+            then 
+                echo "ERROR: target dir [$1] does not exist."
+                exit 1
+            fi
+            echo "set target to $1"
+            client_to="$1"
+        fi
+esac
+
+which rsync >/dev/null || exit 1
+which git >/dev/null || exit 1
+
+echo $line
+echo ">>> #1 of 3 >>> update local git data"
+echo
+echo "URL $git_repo_url"
+echo "TO  $git_target"
+if ! _gitUpdate "$git_repo_url" "$git_target"
+then 
+    echo ERROR occured :-/
+    exit 1
+fi
+echo
+
+
+echo $line
+echo ">>> #2 of 3 >>> Sync files of Appmonitor client"
+echo
+echo "FROM $client_from/*" 
+echo "TO   $client_to"
+rsync -rav \
+    --exclude "build" \
+    --exclude "*.sample.*" \
+    --exclude "example.json" \
+    --exclude "check-appmonitor-server.php" \
+    $client_from/* "$client_to"
+echo
+
+_fileupdate general_include.sample.php
+
+echo $line
+echo ">>> #3 of 3 >>> Diff"
+echo
+diff -r "$client_from" "$client_to"
+echo
+
+
+echo $line
+echo done.
+
+# ----------------------------------------------------------------------
diff --git a/public_html/appmonitor/index.sample.php b/public_html/appmonitor/index.sample.php
deleted file mode 100644
index e3894bb4a0714724714b574d0236845557e1c977..0000000000000000000000000000000000000000
--- a/public_html/appmonitor/index.sample.php
+++ /dev/null
@@ -1,55 +0,0 @@
-<?php
-/* ______________________________________________________________________
- * 
- * A P P M O N I T O R  ::  CLIENT - CHECK  ::  SAMPLE
- * ______________________________________________________________________
- * 
- * this is a sample file for the appmonitor client
- * copy the sample file to index.php and modify it as needed (see ../readme.md).
- * 
- */
-
-require_once('classes/appmonitor-client.class.php');
-$oMonitor = new appmonitor();
-
-// set a name with application name and environment or hostname
-$oMonitor->setWebsite('[My CMS on host XY]');
-
-// how often the server should ask for updates
-$oMonitor->setTTL(300);
-
-// a general include ... the idea is to a file with the same actions on all
-// installations and hosts that can be deployed by a software delivery service 
-// (Puppet, Ansible, ...)
-@include 'general_include.php';
-
-// add any tag to add it in the filter list in the server web gui
-// $oMonitor->addTag('cms');
-// $oMonitor->addTag('production');
-
-// ----------------------------------------------------------------------
-
-// include default checks for an application
-// @require 'plugins/apps/[name-of-app].php';
-
-// add a few custom checks
-// $oMonitor->addCheck(...)
-$oMonitor->addCheck(
-    array(
-        "name" => "hello plugin",
-        "description" => "Test a plugin ... plugins/checks/hello.php",
-        "check" => array(
-            "function" => "Hello",
-            "params" => array(
-                "message" => "Here I am",
-            ),
-        ),
-    )
-);
-
-// ----------------------------------------------------------------------
-
-$oMonitor->setResult();
-$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 603be84aa5f1c778173c701f4501a238c1a50bac..d4e9614be774e3530791c6541d72bc211a854bd2 100644
--- a/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php
+++ b/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php
@@ -64,6 +64,22 @@ $oMonitor->addCheck(
         ),
     )
 );
+
+$oMonitor->addCheck(
+    array(
+        "name" => "PHP modules",
+        "description" => "Check needed PHP modules",
+        // "group" => "folder",
+        "check" => array(
+            "function" => "Phpmodules",
+            "params" => array(
+                "required" => ["curl"],
+                "optional" => [],
+            ),
+        ),
+    )
+);
+
 // ----------------------------------------------------------------------
 // protect dirs against web access
 // specialty: if the test results in an error, the total result switches
diff --git a/public_html/appmonitor/plugins/checks/cert.php b/public_html/appmonitor/plugins/checks/cert.php
index f75e60b877735da9f840a9a5c776c4c0d2f4e6e6..ec5cca3331719fcdc170ad2bc92ba4a514702f30 100644
--- a/public_html/appmonitor/plugins/checks/cert.php
+++ b/public_html/appmonitor/plugins/checks/cert.php
@@ -29,7 +29,7 @@
  *             "function" => "Cert",
  *             "params" => array(
  *                 "url" => "https://www.example.com",
- *                 "warning" => "21",
+ *                 "warning" => "30",
  *             ),
  *         ),
  *     )
@@ -37,6 +37,8 @@
  * ____________________________________________________________________________
  * 
  * 2021-10-26  <axel.hahn@iml.unibe.ch>
+ * 2022-05-02  <axel.hahn@iml.unibe.ch>  set warning to 21 days (old value was 30); add "critical" param
+ * 2022-05-03  <axel.hahn@iml.unibe.ch>  critical limit is a warning only (because app is still functional)
  * 
  */
 class checkCert extends appmonitorcheck{
@@ -55,7 +57,8 @@ class checkCert extends appmonitorcheck{
      * array(
      *     "url"       optional: url to connect check; default: own protocol + server
      *     "verify"    optional: flag for verification of certificate or check for expiration only; default=true (=verification is on)
-     *     "warning"   optional: count of days to warn; default=30
+     *     "warning"   optional: count of days to warn; default=21 (=3 weeks)
+     *     "critical"  optional: count of days to raise critical; default=5
      * )
      * @return boolean
      */
@@ -64,8 +67,9 @@ class checkCert extends appmonitorcheck{
                 ? $aParams["url"] 
                 : 'http'. ($_SERVER['HTTPS'] ? 's' : '') . '://' . $_SERVER['SERVER_NAME'] .':' . $_SERVER['SERVER_PORT']
                 ;
-        $bVerify = isset($aParams["verify"])  ? !!$aParams["verify"] : true;
-        $iWarn   = isset($aParams["warning"]) ? (int)($aParams["warning"]) : 30;
+        $bVerify =   isset($aParams["verify"])   ? !!$aParams["verify"]        : true;
+        $iWarn   =   isset($aParams["warning"])  ? (int)($aParams["warning"])  : 21;
+        $iCrtitcal = isset($aParams["critical"]) ? (int)($aParams["critical"]) : 5;
 
         $sMessage="Checked url: $sUrl ... ";
         $certinfo=$this->_certGetInfos($sUrl, $bVerify);
@@ -91,7 +95,7 @@ class checkCert extends appmonitorcheck{
                 . ' to '.date("Y-m-d H:i", $certinfo['validTo_time_t']).' '
                 . ( $iDaysleft ? "($iDaysleft days left)" : "expired since ".(-$iDaysleft)." days.")
                 ;
-        if ($iDaysleft<0) {
+        if ($iDaysleft<=0) {
             return [
                 RESULT_ERROR, 
                 'Expired! ' . $sMessage
@@ -100,7 +104,10 @@ class checkCert extends appmonitorcheck{
         if ($iDaysleft<=$iWarn) {
             return [
                 RESULT_WARNING, 
-                'Expires soon. ' . $sMessage
+                ($iDaysleft<=$iCrtitcal
+                 ? 'Expires very soon! '
+                 : 'Expires soon. ' 
+                ). $sMessage
             ];
         }
         // echo '<pre>';
diff --git a/public_html/appmonitor/plugins/checks/phpmodules.php b/public_html/appmonitor/plugins/checks/phpmodules.php
new file mode 100644
index 0000000000000000000000000000000000000000..6f40ba054819294542910d2826f25db6a384b42a
--- /dev/null
+++ b/public_html/appmonitor/plugins/checks/phpmodules.php
@@ -0,0 +1,88 @@
+<?php
+/**
+ * ____________________________________________________________________________
+ * 
+ *  _____ _____ __                   _____         _ _           
+ * |     |     |  |      ___ ___ ___|     |___ ___|_| |_ ___ ___ 
+ * |-   -| | | |  |__   | .'| . | . | | | | . |   | |  _| . |  _|
+ * |_____|_|_|_|_____|  |__,|  _|  _|_|_|_|___|_|_|_|_| |___|_|  
+ *                          |_| |_|                              
+ *                           _ _         _                                            
+ *                       ___| |_|___ ___| |_                                          
+ *                      |  _| | | -_|   |  _|                                         
+ *                      |___|_|_|___|_|_|_|   
+ *                                                               
+ * ____________________________________________________________________________
+ * 
+ * CHECK IF NEEDED PHP MODULES ARE INSTALLED
+ * ____________________________________________________________________________
+ * 
+ * 2022-05-06  <axel.hahn@iml.unibe.ch>  first lines
+ * 
+ */
+class checkPhpmodules extends appmonitorcheck{
+    /**
+     * get default group of this check
+     * @param array   $aParams
+     * @return array
+     */
+    public function getGroup(){
+        return 'service';
+    }
+
+    /**
+     * check if system is listening to a given port
+     * @param array $aParams
+     * array(
+     *     required     array  list of required php modules
+     *     optional     array  optional: list of optional php modules
+     * )
+     * @return boolean
+     */
+    public function run($aParams) {
+        $sOut='';
+        $bHasError=false;
+        $bHasWarning=false;
+        // $this->_checkArrayKeys($aParams, "required");
+
+        // --- get all modules
+        $aAllMods=get_loaded_extensions(false);
+        
+        // --- check required modules
+        if(isset($aParams['required']) && count($aParams['required'])){
+            $sOut.='Required: ';
+            foreach($aParams['required'] as $sMod){
+                $sOut.=$sMod.'=';
+                if(!array_search($sMod, $aAllMods)===false){
+                    $sOut.='OK;';
+                } else {
+                    $bHasError=true;
+                    $sOut.='MISS;';
+                }
+            }
+        }
+        // --- check optional modules
+        if(isset($aParams['optional']) && count($aParams['optional'])){
+            $sOut.=($sOut ? '|' : '') . 'Optional: ';
+            foreach($aParams['optional'] as $sMod){
+                $sOut.=$sMod.'=';
+                if(!array_search($sMod, $aAllMods)===false){
+                    $sOut.='OK;';
+                } else {
+                    $bHasWarning=true;
+                    $sOut.='MISS;';
+                }
+            }
+        }
+
+        // --- return result
+        if($bHasError){
+            return [RESULT_ERROR, "ERROR: " . $sOut];
+        }
+        if($bHasWarning){
+            return [RESULT_WARNING, "WARNING: " . $sOut];
+        }
+        return [RESULT_OK, "OK: " . $sOut];
+    }
+    
+}