diff --git a/public_html/deployment/classes/cache.class.php b/public_html/deployment/classes/cache.class.php
index 7077f416eda033b727634065f24cfd019df5bf09..37e815fd5ad722e2e32ce965d118e24dc3839afe 100644
--- a/public_html/deployment/classes/cache.class.php
+++ b/public_html/deployment/classes/cache.class.php
@@ -1,6 +1,16 @@
 <?php
+
 /** 
+ * --------------------------------------------------------------------------------<br>
+ *          __    ______           __       
+ *   ____ _/ /_  / ____/___ ______/ /_  ___ 
+ *  / __ `/ __ \/ /   / __ `/ ___/ __ \/ _ \
+ * / /_/ / / / / /___/ /_/ / /__/ / / /  __/
+ * \__,_/_/ /_/\____/\__,_/\___/_/ /_/\___/ 
+ *                                        
+ * --------------------------------------------------------------------------------<br>
  * AXELS CACHE CLASS<br>
+ * --------------------------------------------------------------------------------<br>
  * <br>
  * THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE <br>
  * LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR <br>
@@ -25,469 +35,853 @@
  *                  - _cleanup checks with file_exists<br>
  * 2014-03-31  2.3  - added _setup() that to includes custom settings<br>
  *                  - limit number of files in cache directory<br>
+ * 2019-11-24  2.4  - added getCachedItems() to get a filtered list of cache files<br>
+ *                  - added remove file to make complete cache of a module invalid<br>
+ *                  - rename var in cache.class_config.php to "$this->_sCacheDirDivider"<br>
+ * 2019-11-26  2.5  - added getModules() to get a list of existing modules that stored<br>
+ *                    a cached item<br>
+ * 2021-09-28  2.6  added a simple admin UI; the cache class got a few new methods
+ *                  - update: cleanup() now always deletes expired items
+ *                  - update: dump() styles output as table
+ *                  - added: getCurrentModule 
+ *                  - added: deleteModule 
+ *                  - added: loadCachefile
+ *                  - added: removefileDelete
+ *                  - added: setCacheId
+ *                  - added: setModule
+ * 2021-09-30  2.7  FIX: remove chdir() in _readCacheItem()
+ * 2021-10-07  2.8  FIX: remove chdir() in _readCacheItem()
+ *                  ADD reference file to expire a cache item
+ *                  - added: getRefFile
+ *                  - added: setRefFile
+ *                  - update: dump, isExpired, isNewerThanFile, write
+ *                  - update cache admin
+ * 2023-03-17  2.9  FIX: harden _getAllCacheData to prevent PHP warnings
+ * 2023-06-02  2.10 shorten code: defaults using ??; short array syntax
+ * 2023-11-20  2.11 check data subkey before writing
+ * --------------------------------------------------------------------------------<br>
+ * compatible to PHP 7 ... 8.2<br>
  * --------------------------------------------------------------------------------<br>
- * @version 2.3
+ * @version 2.11
  * @author Axel Hahn
- * @link http://www.axel-hahn.de/php_contentcache
+ * @link https://www.axel-hahn.de/docs/ahcache/index.htm
  * @license GPL
  * @license http://www.gnu.org/licenses/gpl-3.0.html GPL 3.0
  * @package Axels Cache
  */
-class AhCache {
-
-    /**
-     * a module name string is used as relative cache path
-     * @var string 
-     */
-    var $sModule = '';
-
-    /**
-     * id of cachefile (filename will be generated from it)
-     * @var string 
-     */
-    var $sCacheID = '';
-
-    /**
-     * where to store all cache data - it can be outside docRoot. If it is 
-     * below docRoot think about forbidding access
-     * If empty it will be set in the constructor to [webroot]/~cache/
-     * or $TEMP/~cache/ for CLI
-     * @var string 
-     */
-    private $_sCacheDir = '';
-    // private $_sCacheDir='/tmp/';
-
-    /**
-     * absolute filename of cache file
-     * @var string 
-     */
-    private $_sCacheFile = '';
-
-    /**
-     * divider to limit count of cachefiles
-     * @var type 
-     */
-    private $_sCacheDirDevider = false;
-    
-    /**
-     * fileextension for storing cachefiles (without ".")
-     * @var string
-     */
-    private $_sCacheExt = 'cacheclass2';
-
-    /**
-     * Expiration timestamp; 
-     * It will be calculated with current time + ttl in the write() method
-     * TTL can be read with getExpire()
-     * @var integer
-     */
-    private $_tsExpire = -1;
-
-    /**
-     * TTL (time to live) in s; 
-     * TTL can be set in methods setTtl($iTtl) or write($data, $iTtl)
-     * TTL can be read with getTtl()
-     * @var integer
-     */
-    private $_iTtl = -1;
-
-    /**
-     * cachedata and file infos of cachefile (returned array of php function stat)
-     * @var array
-     */
-    private $_aCacheInfos = array();
-
-    /* ----------------------------------------------------------------------
+if (!class_exists("AhCache")) {
+    class AhCache
+    {
+
+        /**
+         * a module name string is used as relative cache path
+         * @var string 
+         */
+        var $sModule = '';
+
+        /**
+         * id of cachefile (filename will be generated from it)
+         * @var string 
+         */
+        var $sCacheID = '';
+
+        /**
+         * where to store all cache data - it can be outside docRoot. If it is 
+         * below docRoot think about forbidding access
+         * If empty it will be set in the constructor to [webroot]/~cache/
+         * or $TEMP/~cache/ for CLI
+         * @var string 
+         */
+        private $_sCacheDir = '';
+
+        /**
+         * absolute filename of cache file
+         * @var string 
+         */
+        private $_sCacheFile = '';
+
+        /**
+         * divider to limit count of cachefiles
+         * @var type 
+         */
+        private $_sCacheDirDivider = false;
+
+        /**
+         * fileextension for storing cachefiles (without ".")
+         * @var string
+         */
+        private $_sCacheExt = 'cacheclass2';
+
+        /**
+         * Expiration timestamp; 
+         * It will be calculated with current time + ttl in the write() method
+         * TTL can be read with getExpire()
+         * @var integer
+         */
+        private $_tsExpire = -1;
+
+        /**
+         * TTL (time to live) in s; 
+         * TTL can be set in methods setTtl($iTtl) or write($data, $iTtl)
+         * TTL can be read with getTtl()
+         * @var integer
+         */
+        private $_iTtl = -1;
+
+        /**
+         * cachedata and file infos of cachefile (returned array of php function stat)
+         * @var array
+         */
+        private $_aCacheInfos = [];
+
+        /**
+         * Full path to a cache remove file for the current module
+         * @var string
+         */
+        private $_sCacheRemovefile = false;
+
+        /**
+         * Reference file: a cache is outdated if the reference file is newer than
+         * the cache item.
+         * TTL can be set in methods setRefFile($sFile) or write($data, $iTtl, $sFile)
+         * TTL can be read with getRefFile()
+         * @var string
+         */
+        private $_sRefFile = false;
+
+        /* ----------------------------------------------------------------------
       constructor
       ---------------------------------------------------------------------- */
 
-    /**
-     * constructor
-     * @param  string  $sModule   name of module or app that uses the cache
-     * @param  string  $sCacheID  cache-id (must be uniq for a module; used to generate filename of cachefile)
-     * @return boolean 
-     */
-    public function __construct($sModule = ".", $sCacheID = '.') {
-        $this->_setup();
-        if (!$sModule)
-            die("ERROR: no module was given.<br>");
-        if (!$sCacheID)
-            die("ERROR: no id was given.<br>");
-
-        $this->sModule = $sModule;
-        $this->sCacheID = $sCacheID;
-
-        $this->_getCacheFilename();
-        $this->read();
-
-        return true;
-    }
+        /**
+         * constructor
+         * @param  string  $sModule   name of module or app that uses the cache
+         * @param  string  $sCacheID  cache-id (must be uniq within a module; used to generate filename of cachefile)
+         * @return boolean 
+         */
+        public function __construct($sModule = ".", $sCacheID = '.')
+        {
+            $this->setModule($sModule, $sCacheID);
+            return true;
+        }
 
-    /* ----------------------------------------------------------------------
+        /* ----------------------------------------------------------------------
       private funtions
       ---------------------------------------------------------------------- */
 
-    // ----------------------------------------------------------------------
-    /**
-     * load custom config from cache.class_config.php and set a cache
-     * directory
-     */
-    private function _setup() {
-        $sCfgfile="cache.class_config.php";
-        if (file_exists(__DIR__ . "/".$sCfgfile)){
-            include($sCfgfile);
-        }
-        if (!$this->_sCacheDir) {
-            if (getenv("TEMP"))
-                $this->_sCacheDir = str_replace("\\", "/", getenv("TEMP"));
-            if ($_SERVER['DOCUMENT_ROOT'])
-                $this->_sCacheDir = $_SERVER['DOCUMENT_ROOT'];
-            if (!$this->_sCacheDir)
-                $this->_sCacheDir = ".";
-            $this->_sCacheDir .= "/~cache";
-        }
-    }
-            
-    // ----------------------------------------------------------------------
-    /**
-     * recursive cleanup of a given directory; this function is used by
-     * public function cleanup()
-     * @since 2.0
-     * @param string $sDir full path of a local directory
-     * @param string $iTS  timestamp
-     * @return     true
-     */
-    private function _cleanupDir($sDir, $iTS) {
-        echo "<ul><li class=\"dir\">DIR: <strong>$sDir</strong><ul>";
-
-        if (!file_exists($sDir)) {
-            echo "\t Directory does not exist - [$sDir]</ul></li></ul>";
-            return;
-        }
-        if (!($d = dir($sDir))) {
-            echo "\t Cannot open directory - [$sDir]</ul></li></ul>";
-            return;
-        }
-        while ($entry = $d->read()) {
-            $sEntry = $sDir . "/" . $entry;
-            if (is_dir($sEntry) && $entry != '.' && $entry != '..') {
-                $this->_cleanupDir($sEntry, $iTS);
-            }
-            if (file_exists($sEntry)) {
-                $ext = pathinfo($sEntry, PATHINFO_EXTENSION);
-                $ext = substr($sEntry, strrpos($sEntry, '.') + 1);
-
-                $exts = explode(".", $sEntry);
-                $n = count($exts) - 1;
-                $ext = $exts[$n];
-
-                if ($ext == $this->_sCacheExt) {
-
-                    $aTmp = stat($sEntry);
-                    $iAge = date("U") - $aTmp['mtime'];
-                    if ($aTmp['mtime'] <= $iTS) {
-                        echo "<li class=\"delfile\">delete cachefile: $sEntry ($iAge s)<br>";
-                        unlink($sEntry);
-                    } else {
-                        echo "<li class=\"keepfile\">keep cachefile: $sEntry ($iAge s; " . ($aTmp['mtime'] - $iTS) . " s left)</li>";
+        // ----------------------------------------------------------------------
+        /**
+         * init
+         * - load custom config from cache.class_config.php 
+         * - set a cache
+         * - set remove file (if does not exist)
+         * directory
+         */
+        private function _setup()
+        {
+            if (!$this->sModule) {
+                die("ERROR: no module was given.<br>");
+            }
+            if (!$this->sCacheID) {
+                die("ERROR: no id was given.<br>");
+            }
+            $sCfgfile = "cache.class_config.php";
+            if (file_exists(__DIR__ . "/" . $sCfgfile)) {
+                include(__DIR__ . "/" . $sCfgfile);
+            }
+            if (!$this->_sCacheDir) {
+                if (getenv("TEMP"))
+                    $this->_sCacheDir = str_replace("\\", "/", getenv("TEMP"));
+                if ($_SERVER['DOCUMENT_ROOT'])
+                    $this->_sCacheDir = $_SERVER['DOCUMENT_ROOT'];
+                if (!$this->_sCacheDir)
+                    $this->_sCacheDir = ".";
+                $this->_sCacheDir .= "/~cache";
+            }
+            $this->_sCacheRemovefile = $this->_sCacheDir . '/' . $this->sModule . '/__remove_me_to_make_caches_invalid__';
+            if (!file_exists($this->_sCacheRemovefile)) {
+                if (!is_dir($this->_sCacheDir . '/' . $this->sModule)) {
+                    if (!mkdir($this->_sCacheDir . '/' . $this->sModule, 0750, true)) {
+                        die("ERROR: unable to create directory " . $this->_sCacheDir . '/' . $this->sModule);
                     }
                 }
+                touch($this->_sCacheRemovefile);
             }
+            return true;
         }
-        echo "</ul></li></ul>";
 
-        // try to delete if it should be empty
-        @rmdir($sDir);
-        return true;
-    }
+        // ----------------------------------------------------------------------
+        /**
+         * private function _getAllCacheData() - read cachedata and its meta infos
+         * @since 2.0
+         * @return     array  array with data, file stat
+         */
+        private function _getAllCacheData()
+        {
+            if (!$this->_sCacheFile) {
+                return false;
+            }
+            $this->_aCacheInfos = [];
+            $aTmp = $this->_readCacheItem($this->_sCacheFile);
+            if (is_array($aTmp) && count($aTmp)) {
+                $this->_aCacheInfos['data'] = $aTmp['data'] ?? false;
+                $this->_iTtl                = $aTmp['iTtl']        ? $aTmp['iTtl']     : -1;
+                $this->_tsExpire            = $aTmp['tsExpire']    ? $aTmp['tsExpire'] : -1;
+
+                $this->_sRefFile = $aTmp['sRefFile'] ?? false;
+                $this->_aCacheInfos['stat'] = stat($this->_sCacheFile);
+
+                // @see loadCachefile: it sets module + id to false
+                $this->sModule = $this->sModule ? $this->sModule : $aTmp['module'];
+                $this->sCacheID = $this->sCacheID ? $this->sCacheID : $aTmp['cacheid'];
+            }
+            return $this->_aCacheInfos;
+        }
 
-    // ----------------------------------------------------------------------
-    /**
-     * private function _getAllCacheData() - read cachedata and its meta infos
-     * @since 2.0
-     * @return     array  array with data, file stat
-     */
-    private function _getAllCacheData() {
-        if (!$this->_sCacheFile)
+        /**
+         * read a raw cache item and return it as hash
+         *
+         * @param string  $sFile  filename with full path or relative to cache base path
+         * @return array|boolean
+         */
+        private function _readCacheItem($sFile)
+        {
+            $sFull = file_exists($sFile) ? $sFile : $this->_sCacheDir . '/' . $sFile;
+            if (file_exists($sFull)) {
+                return unserialize(file_get_contents($sFull));
+            }
             return false;
-        $this->_aCacheInfos = array();
-        if (file_exists($this->_sCacheFile)) {
-            $aTmp = unserialize(file_get_contents($this->_sCacheFile));
-            $this->_aCacheInfos['data'] = $aTmp['data'];
-            $this->_iTtl = $aTmp['iTtl'];
-            $this->_tsExpire = $aTmp['tsExpire'];
-            $this->_aCacheInfos['stat'] = stat($this->_sCacheFile);
-        }
-        return $this->_aCacheInfos;
-    }
+        }
 
-    // ----------------------------------------------------------------------
-    /**
-     * private function _getCacheFilename() - get full filename of cachefile
-     * @return     string  full filename of cachefile
-     */
-    private function _getCacheFilename() {
-        $sMyFile=md5($this->sCacheID);
-        if($this->_sCacheDirDevider && $this->_sCacheDirDevider>0){
-            $sMyFile=preg_replace('/([0-9a-f]{'.$this->_sCacheDirDevider.'})/', "$1/", $sMyFile);
-        }
-        $sMyFile.=".".$this->_sCacheExt;
-        $sMyFile=str_replace("/.", ".", $sMyFile);
-        // $this->_sCacheFile = $this->_sCacheDir . "/" . $this->sModule . "/" . md5($this->sCacheID) . "." . $this->_sCacheExt;
-        $this->_sCacheFile = $this->_sCacheDir . "/" . $this->sModule . "/" . $sMyFile;
-        return $this->_sCacheFile;
-    }
+        // ----------------------------------------------------------------------
+        /**
+         * private function _getCacheFilename() - get full filename of cachefile
+         * @return     string  full filename of cachefile
+         */
+        private function _getCacheFilename()
+        {
+            $sMyFile = md5($this->sCacheID);
+            if ($this->_sCacheDirDivider && $this->_sCacheDirDivider > 0) {
+                $sMyFile = preg_replace('/([0-9a-f]{' . $this->_sCacheDirDivider . '})/', "$1/", $sMyFile);
+            }
+            $sMyFile .= "." . $this->_sCacheExt;
+            $sMyFile = str_replace("/.", ".", $sMyFile);
+            // $this->_sCacheFile = $this->_sCacheDir . "/" . $this->sModule . "/" . md5($this->sCacheID) . "." . $this->_sCacheExt;
+            $this->_sCacheFile = $this->_sCacheDir . "/" . $this->sModule . "/" . $sMyFile;
+            return $this->_sCacheFile;
+        }
 
-    /* ----------------------------------------------------------------------
+        /* ----------------------------------------------------------------------
       public funtions
       ---------------------------------------------------------------------- */
 
-    // ----------------------------------------------------------------------
-    /**
-     * Cleanup cache directory; delete all cachefiles older than n seconds
-     * Other filetypes in the directory won't be touched.
-     * Empty directories will be deleted.
-     * 
-     * Only the directory of the initialized module/ app will be deleted.
-     * $o=new Cache("my-app"); $o->cleanup(60*60*24*3); 
-     * 
-     * To delete all cachefles of all modules you can use
-     * $o=new Cache(); $o->cleanup(0); 
-     * 
-     * @since 2.0
-     * @param int $iSec max age of cachefile; older cachefiles will be deleted
-     * @return     true
-     */
-    public function cleanup($iSec = false) {
-        echo date("d.m.y - H:i:s") . " START CLEANUP $this->_sCacheDir/" . $this->sModule . ", $iSec s<br>
-                <style>
-                    .dir{color:#888;}
-                    .delfile{color:#900;}
-                    .keepfile{color:#ccc;}
-                </style>";
-        $this->_cleanupDir($this->_sCacheDir . "/" . $this->sModule, date("U") - $iSec);
-        echo date("d.m.y - H:i:s") . " END CLEANUP <br>";
-        return true;
-    }
-
-    // ----------------------------------------------------------------------
-    /**
-     * public function delete - delete a single cachefile if it exist
-     * @return     boolean
-     */
-    public function delete() {
-        if (!file_exists($this->_sCacheFile))
+        /**
+         * helper function - remove empty cache directories up to module cache dir
+         *
+         * @param string    $sDir
+         * @param boolean   $bShowOutput   flag: show output? default: false (=no output)
+         * @return void
+         */
+        private function _removeEmptyCacheDir($sDir, $bShowOutput = false)
+        {
+            // echo $bShowOutput ? __METHOD__."($sDir)<br>\n" : '';
+            if ($sDir > $this->_sCacheDir . "/" . $this->sModule) {
+                echo $bShowOutput ? "REMOVE DIR [$sDir] ... " : '';
+                if (is_dir($sDir)) {
+                    if (rmdir($sDir)) {
+                        chdir($this->_sCacheDir);
+                        echo $bShowOutput ? "OK<br>\n" : '';
+                        usleep(20000); // 0.02 sec
+                        $this->_removeEmptyCacheDir(dirname($sDir), $bShowOutput);
+                    } else {
+                        echo $bShowOutput ? "failed.<br>\n" : '';
+                        return false;
+                    }
+                } else {
+                    echo $bShowOutput ? "skip: not a directory.<br>\n" : '';
+                }
+                return true;
+            }
             return false;
-        if (unlink($this->_sCacheFile)) {
-            $this->_aCacheInfos['data'] = false;
-            $this->_aCacheInfos['stat'] = false;
+        }
+
+        /* ----------------------------------------------------------------------
+      public funtions
+      ---------------------------------------------------------------------- */
+
+        // ----------------------------------------------------------------------
+        /**
+         * Cleanup cache directory; It deletes all outdated cache items of current module.
+         * Additionally you can delete all cachefiles older than n seconds (because
+         * there are cache items that are not based on a TTL).
+         * Other filetypes in the cache directory won't be touched.
+         * Empty directories will be deleted.
+         * 
+         * Only the directory of the initialized module/ app will be deleted.
+         * $o=new Cache("my-app"); $o->cleanup(60*60*24*3); 
+         * 
+         * To delete all cachefles of all modules you can use
+         * $o=new Cache(); $o->cleanup(0); 
+         * 
+         * @since 2.0
+         * @param int       $iSec          max age of cachefile; older cachefiles will be deleted
+         * @param boolean   $bShowOutput   flag: show output? default: false (=no output)
+         * @return     true
+         */
+        public function cleanup($iSec = false, $bShowOutput = false)
+        {
+            // quick and dirty
+            $aFilter = ['lifetimeBelow' => 0];
+            if (!$iSec === false) {
+                $aFilter['ageOlder'] = $iSec;
+            }
+            $aData = $this->getCachedItems(false, $aFilter);
+            echo $bShowOutput ? 'CLEANUP  ' . count($aData) . " files\n" : '';
+            if ($aData) {
+                $aFiles = array_keys($aData);
+                rsort($aFiles);
+                foreach (array_keys($aData) as $sFile) {
+                    echo $bShowOutput ? 'DELETE ' . $sFile . "<br>\n" : '';
+                    unlink($sFile);
+                    $this->_removeEmptyCacheDir(dirname($sFile), $bShowOutput);
+                }
+            }
             return true;
         }
-        return false;
-    }
 
-    // ----------------------------------------------------------------------
-    /**
-     * public function dump() - dump variables of cache class
-     * @return     true
-     */
-    public function dump() {
-        echo "
-                <hr>
-                <strong>cache->dump()<br></strong>
-                <strong>module: </strong>" . $this->sModule . "<br>
-                <strong>ID: </strong>" . $this->sCacheID . "<br>
-                <strong>filename: </strong>" . $this->_sCacheFile;
-        if (!file_exists($this->_sCacheFile))
-            echo " (does not exist yet)";
-        echo "<br>
-                <strong>age: </strong>" . $this->getAge() . " s<br>
-                <strong>ttl: </strong>" . $this->getTtl() . " s<br>
-                <strong>expires: </strong>" . $this->getExpire() . " (" . date("d.m.y - H:i:s", $this->getExpire()) . ")<br>
-                <pre>";
-        print_r($this->_aCacheInfos);
-        echo "</pre><hr>";
-        return true;
-    }
+        // ----------------------------------------------------------------------
+        /**
+         * get an array with cached data elements
+         * @since 2.4
+         *
+         * @param string  $sDir     full path of cache dir; default: false (auto detect cache dir)
+         * @param array   $aFilter  filter; valid keys are
+         *                          - ageOlder         integer  return items that are older [n] sec
+         *                          - lifetimeBelow    integer  return items that expire in less [n] sec (or outdated)
+         *                          - lifetimeGreater  integer  return items that expire in more than [n] sec
+         *                          - ttlBelow         integer  return items with ttl less than [n] sec
+         *                          - ttlGreater       integer  return items with ttl more than [n] sec
+         *                          no filter returns all cached entries
+         * @return array
+         */
+        public function getCachedItems($sDir = false, $aFilter = [])
+        {
+            $aReturn = [];
+            $sDir = $sDir ? $sDir : $this->_sCacheDir . "/" . $this->sModule;
+            if (!file_exists($sDir)) {
+                // echo "\t Directory does not exist - [$sDir]";
+                return false;
+            }
+            if (!($d = dir($sDir))) {
+                // echo "\t Cannot open directory - [$sDir]</ul></li></ul>";
+                return;
+            }
+            while ($entry = $d->read()) {
+                $sEntry = $sDir . "/" . $entry;
+                if (is_dir($sEntry) && $entry != '.' && $entry != '..') {
+                    $aReturn = array_merge($aReturn, $this->getCachedItems($sEntry, $aFilter));
+                }
 
-    // ----------------------------------------------------------------------
-    /**
-     * public function getCacheAge() - get age in seconds of exisiting cachefile
-     * @return     int  age in seconds; -1 if cachefiles does not exist
-     */
-    public function getAge() {
-        if (!isset($this->_aCacheInfos['stat']))
-            $this->_getAllCacheData();
-        if (!isset($this->_aCacheInfos['stat']))
-            return -1;
-        return date("U") - $this->_aCacheInfos['stat']['mtime'];
-    }
+                if (file_exists($sEntry)) {
+                    $ext = pathinfo($sEntry, PATHINFO_EXTENSION);
+                    $ext = substr($sEntry, strrpos($sEntry, '.') + 1);
+
+                    $exts = explode(".", $sEntry);
+                    $n = count($exts) - 1;
+                    $ext = $exts[$n];
+
+                    if ($ext == $this->_sCacheExt) {
+
+                        $aData = $this->_readCacheItem($sEntry);
+                        unset($aData['data']);
+
+                        $aData['_lifetime'] = $aData['tsExpire'] - date('U');
+                        $aData['_age'] = date('U') - filemtime($sEntry);
+                        $aData['_lifetime'] = filemtime($sEntry) > filemtime($this->_sCacheRemovefile) ? $aData['tsExpire'] - date('U') : -1;
+
+                        $bAdd = false;
+
+                        if (isset($aFilter['ageOlder']) && ($aData['_age'] > $aFilter['ageOlder'])) {
+                            $bAdd = true;
+                        }
+                        if (isset($aFilter['lifetimeBelow']) && ($aData['_lifetime'] < $aFilter['lifetimeBelow'])) {
+                            $bAdd = true;
+                        }
+                        if (isset($aFilter['lifetimeGreater']) && ($aData['_lifetime'] > $aFilter['lifetimeGreater'])) {
+                            $bAdd = true;
+                        }
+                        if (isset($aFilter['ttlBelow']) && ($aData['iTtl'] < $aFilter['ttlBelow'])) {
+                            $bAdd = true;
+                        }
+                        if (isset($aFilter['ttlGreater']) && ($aData['iTtl'] > $aFilter['ttlGreater'])) {
+                            $bAdd = true;
+                        }
+
+                        if (!is_array($aFilter) || !count($aFilter)) {
+                            $bAdd = true;
+                        }
+
+                        if ($bAdd) {
+                            $aReturn[$sEntry] = $aData;
+                        }
+                    }
+                }
+            }
+            return $aReturn;
+        }
 
-    // ----------------------------------------------------------------------
-    /**
-     * public function getExpire() - get TS of cache expiration
-     * @since 2.0
-     * @return     int  unix ts of cache expiration
-     */
-    public function getExpire() {
-        return $this->_tsExpire;
-    }
+        // ----------------------------------------------------------------------
+        /**
+         * get currently activated module
+         * @since 2.6
+         * @return string
+         */
+        public function getCurrentModule()
+        {
+            return $this->sModule;
+        }
 
-    // ----------------------------------------------------------------------
-    /**
-     * public function getTtl() - get TTL of cache in seconds
-     * @since 2.0
-     * @return     int  get ttl of cache
-     */
-    public function getTtl() {
-        return $this->_iTtl;
-    }
+        // ----------------------------------------------------------------------
+        /**
+         * get current cache id
+         * @since 2.6
+         * @return string
+         */
+        public function getCurrentId()
+        {
+            return $this->sCacheID;
+        }
 
-    // ----------------------------------------------------------------------
-    /**
-     * public function isExpired() - cache expired? To check it 
-     * you must use ttl while writing data, i.e.
-     * $oCache->write($sData, $iTtl);
-     * @since 2.0
-     * @return     bool  cache is expired?
-     */
-    public function isExpired() {
-        if (!$this->_tsExpire)
-            return true;
-        return ((date("U") - $this->_tsExpire)>0);
-    }
-    // ----------------------------------------------------------------------
-    /**
-     * public function iExpired() - get time in seconds when cachefile expires
-     * you must use ttl while writing data, i.e.
-     * $oCache->write($sData, $iTtl);
-     * @since 2.1
-     * @return     int  expired time in seconds; negative if cache is not expired
-     */
-    public function iExpired() {
-        if (!$this->_tsExpire)
-            return true;
-        return date("U") - $this->_tsExpire;
-    }
+        // ----------------------------------------------------------------------
+        /**
+         * get a flat array of module names that saved a cache item already
+         * @since 2.5
+         * 
+         * @return array
+         */
+        public function getModules()
+        {
+            $aReturn = [];
+            foreach (glob($this->_sCacheDir . '/*') as $sEntry) {
+                if (is_dir($sEntry)) {
+                    $aReturn[] = basename($sEntry);
+                }
+            }
+            return $aReturn;
+        }
 
-    // ----------------------------------------------------------------------
-    /**
-     * function isNewerThanFile($sRefFile) - is the cache (still) newer than
-     * a reference file? This function returns difference of mtime of both
-     * files.
-     * @since 2.0
-     * @param   string   $sRefFile  local filename
-     * @return  integer  time in sec how much the cache file is newer; negative if reference file is newer
-     */
-    public function isNewerThanFile($sRefFile) {
-        if (!file_exists($sRefFile))
-            return false;
-        if (!isset($this->_aCacheInfos['stat']))
+        // ----------------------------------------------------------------------
+        /**
+         * delete a single cache item if it exist.
+         * It returns true if a cache item was deleted. It returns false if it
+         * does not exist (yet) or the deletion failed.
+         * @return     boolean
+         */
+        public function delete()
+        {
+            if (!file_exists($this->_sCacheFile)) {
+                return false;
+            }
+            if (unlink($this->_sCacheFile)) {
+                $this->_aCacheInfos['data'] = false;
+                $this->_aCacheInfos['stat'] = false;
+                return true;
+            }
             return false;
+        }
+        // ----------------------------------------------------------------------
+        /**
+         * delete all existing cached items of the set module
+         * Remark: this method should be used in an admin interface or cronjob only.
+         * It makes a recursive filesystem scan and is quite slow.
+         * 
+         * @since 2.6
+         * @param boolean   $bShowOutput   flag: show output? default: false (=no output)
+         * @return     boolean
+         */
+        public function deleteModule($bShowOutput = false)
+        {
+            if (!$this->sModule) {
+                return false;
+            }
+            $this->cleanup(0, $bShowOutput);
+            $this->removefileDelete();
+            return rmdir($this->_sCacheDir . "/" . $this->sModule);
+        }
 
-        $aTmp = stat($sRefFile);
-        $iTimeRef = $aTmp['mtime'];
-        
-        //echo $this->_sCacheFile."<br>".$this->_aCacheInfos['stat']['mtime']."<br>".$iTimeRef."<br>".($this->_aCacheInfos['stat']['mtime'] - $iTimeRef);
-        return $this->_aCacheInfos['stat']['mtime'] - $iTimeRef;
-    }
+        // ----------------------------------------------------------------------
+        /**
+         * public function dump() - dump variables of cache class
+         * @return     true
+         */
+        public function dump()
+        {
+            $sReturn = '';
+            $sReturn .= "<table>
+            <!--<strong>" . __METHOD__ . "()<br></strong>-->
+            <tr><td><strong>module: </strong></td><td>" . $this->sModule . "</td></tr>
+            <tr><td><strong>ID: </strong></td><td>" . $this->sCacheID . "</td></tr>
+            <tr><td><strong>filename: </strong></td><td>" . $this->_sCacheFile . "</td></tr>
+            ";
+            if (file_exists($this->_sCacheFile)) {
+                $sReturn .= "
+				<tr><td><strong>size: </strong></td><td>" . filesize($this->_sCacheFile) . " byte</td></tr>
+                <tr><td><strong>created: </strong></td><td>" . filemtime($this->_sCacheFile) . " (" . date("d.m.y - H:i:s", filemtime($this->_sCacheFile)) . ")</td></tr>
+                <tr><td><strong>ttl: </strong></td><td>" . $this->getTtl() . " s</td></tr>
+                " . ($this->getTtl() < 0
+                    ? ''
+                    : "<tr><td><strong>expires: </strong></td><td>" . $this->getExpire() . " (" . date("d.m.y - H:i:s", $this->getExpire()) . ") "
+                    . ($this->iExpired() > 0 ? '<span class="outdated">' . $this->iExpired() . ' s EXPIRED</span>' : ' ... <span class="ok">' . -$this->iExpired() . " s left") . "</td></tr>"
+                )
+                    . "<tr><td><strong>age: </strong></td><td>" . $this->getAge() . " s</td></tr>"
+                    . "<tr><td><strong>reference file: </strong></td><td>" . ($this->getRefFile() ? $this->getRefFile() : 'NONE') . " </td></tr>"
+                    . "<br>
+				</table>
+                <strong>data in the cache:</strong>
+                <pre>";
+                $sReturn .= htmlentities(print_r($this->_aCacheInfos, 1));
+                $sReturn .= "</pre><hr>";
+            } else {
+                $sReturn .= "</table>Cache file does not exist (yet).<br>
+                Maybe the _sDivider was changed in another cache instance.<br>
+                ";
+            }
+            echo $sReturn;
+            return true;
+        }
 
-    // ----------------------------------------------------------------------
-    /**
-     * public function getCacheData() - read cachedata if it exist
-     * @return     various  cachedata or false if cache does not exist
-     */
-    public function read() {
-        if (!isset($this->_aCacheInfos['data']))
-            $this->_getAllCacheData();
-        if (!isset($this->_aCacheInfos['data']))
-            return false;
-        return $this->_aCacheInfos['data'];
-    }
+        // ----------------------------------------------------------------------
+        /**
+         * public function getCacheAge() - get age in seconds of exisiting cachefile
+         * @return     int  age in seconds; -1 if cachefiles does not exist
+         */
+        public function getAge()
+        {
+            if (!isset($this->_aCacheInfos['stat'])) {
+                $this->_getAllCacheData();
+            }
+            if (!isset($this->_aCacheInfos['stat'])) {
+                return -1;
+            }
+            return date("U") - $this->_aCacheInfos['stat']['mtime'];
+        }
 
-    // ----------------------------------------------------------------------
-    /**
-     * public function setData($data) - set cachedata into cache object
-     * data can be any serializable type, like string, array or object
-     * Remark: You additionally need to call the write() method to store data in the filesystem
-     * @since 2.0
-     * @param      various  $data  data to store in cache
-     * @return   boolean
-     */
-    public function setData($data) {
-        return $this->_aCacheInfos['data'] = $data;
-    }
+        // ----------------------------------------------------------------------
+        /**
+         * public function getExpire() - get TS of cache expiration
+         * @since 2.0
+         * @return     int  unix ts of cache expiration
+         */
+        public function getExpire()
+        {
+            return $this->_tsExpire;
+        }
 
-    // ----------------------------------------------------------------------
-    /**
-     * public function setTtl() - set TTL of cache in seconds
-     * You need to write the cache data to ap
-     * Remark: You additionally need to call the write() method to store a new ttl value with 
-     * data in the filesystem
-     * @since 2.0
-     * @param type $iTtl  ttl value in seconds
-     * @return     int  get ttl of cache
-     */
-    public function setTtl($iTtl) {
-        return $this->_iTtl = $iTtl;
-    }
+        // ----------------------------------------------------------------------
+        /**
+         * public function getRefFile() - get reference file that invalidates the
+         * cache item
+         * @since 2.8
+         * @return     int  get ttl of cache
+         */
+        public function getRefFile()
+        {
+            return $this->_sRefFile;
+        }
 
-    // ----------------------------------------------------------------------
-    /**
-     * public function touch() - touch cachefile if it exist
-     * For cachedata with a ttl a new expiration will be set
-     * @return boolean 
-     */
-    public function touch() {
-        if (!file_exists($this->_sCacheFile))
-            return false;
+        // ----------------------------------------------------------------------
+        /**
+         * public function getTtl() - get TTL of cache in seconds
+         * @since 2.0
+         * @return     int  get ttl of cache
+         */
+        public function getTtl()
+        {
+            return $this->_iTtl;
+        }
 
-        // touch der Datei reicht nicht mehr, weil tsExpire verloren ginge
-        if (!$this->_iTtl)
-            $bReturn = touch($this->_sCacheFile);
-        else
-            $bReturn = $this->write();
 
-        $this->_getAllCacheData();
+        // ----------------------------------------------------------------------
+        /**
+         * public function isExpired() - cache expired? To check it 
+         * you must use ttl while writing data, i.e.
+         * $oCache->write($sData, [$iTtl, [RefFile]]);
+         * A cache item is expired if
+         *   - the module remove file is newer
+         *   - if a ttl was set: the min. ttl  
+         * @since 2.0
+         * @return     bool  cache is expired?
+         */
+        public function isExpired()
+        {
+            // cache data already exist? $this->_aCacheInfos is set by constructor 
+            // in the read method
+            if (!isset($this->_aCacheInfos['data'])) {
+                return true;
+            }
+            // check if remove file was touched
+            $iAgeOfCache = $this->getAge();
+            if ($iAgeOfCache > (date("U") - filemtime($this->_sCacheRemovefile))) {
+                return true;
+            }
+            // check if cahe item is expired
+            if ($this->_tsExpire && (date("U") > $this->_tsExpire)) {
+                return true;
+            }
+            // check timestamp of reference file (if one was set)
+            return !$this->isNewerThanFile();
+        }
+        // ----------------------------------------------------------------------
+        /**
+         * public function iExpired() - get time in seconds when cachefile expires
+         * you must use ttl while writing data, i.e.
+         * $oCache->write($sData, $iTtl);
+         * @since 2.1
+         * @return     int  expired time in seconds; negative if cache is not expired
+         */
+        public function iExpired()
+        {
+            if (!$this->_tsExpire) {
+                return true;
+            }
+            return date("U") - $this->_tsExpire;
+        }
 
-        return $bReturn;
-    }
+        // ----------------------------------------------------------------------
+        /**
+         * function isNewerThanFile($sRefFile) - is the cache (still) newer than
+         * a reference file? This function returns difference of mtime of both
+         * files.
+         * @since 2.0
+         * @param   string   $sRefFile  local filename
+         * @return  integer  time in sec how much the cache file is newer; negative if reference file is newer
+         */
+        public function isNewerThanFile($sRefFile = null)
+        {
+            if (is_null($sRefFile)) {
+                $sRefFile = $this->_sRefFile;
+            }
+            if (!$sRefFile || !file_exists($sRefFile)) {
+                return true;
+            }
+            if (!isset($this->_aCacheInfos['stat'])) {
+                return false;
+            }
+
+            $aTmp = stat($sRefFile);
+            $iTimeRef = $aTmp['mtime'];
+
+            //echo $this->_sCacheFile."<br>".$this->_aCacheInfos['stat']['mtime']."<br>".$iTimeRef."<br>".($this->_aCacheInfos['stat']['mtime'] - $iTimeRef);
+            return $this->_aCacheInfos['stat']['mtime'] - $iTimeRef;
+        }
 
-    // ----------------------------------------------------------------------
-    /**
-     * Write data into a cache. 
-     * - data can be any serializable type, like string, array or object
-     * - set ttl in s (from now); optional parameter
-     * @param      various  $data  data to store in cache
-     * @param      int      $iTtl  time in s if content cache expires (min. 0)
-     * @return     bool     success of write action
-     */
-    public function write($data = false, $iTtl = -1) {
-        if (!$this->_sCacheFile)
+        // ----------------------------------------------------------------------
+        /**
+         * load cache item from a given file - this is like reverse engineering 
+         * by reading data file; needed for a admin interface only
+         * 
+         * @since 2.6
+         * @param string  $sFile  filename with full path
+         * @return boolean
+         */
+        public function loadCachefile($sFile)
+        {
+            $this->_sCacheFile = false;
+            $this->sModule = false;
+            $this->sCacheID = false;
+            if (file_exists($sFile)) {
+                $this->_sCacheFile = $sFile;
+                $this->_getAllCacheData();
+                // reverse engineered _sCacheDirDivider
+                $sRelfile = str_replace($this->_sCacheDir . "/" . $this->sModule, '', $this->_sCacheFile);
+                $sTmp = preg_replace('#^\/([0-9a-f]*)([/\.].*)#', '$1', $sRelfile);
+                $this->_sCacheDirDivider = strlen($sTmp);
+                return true;
+            }
             return false;
+        }
 
-        $sDir = dirname($this->_sCacheFile);
-        if (!is_dir($sDir))
-            if (!mkdir($sDir, 0750, true))
-                die("ERROR: unable to create directory " . $sDir);
+        // ----------------------------------------------------------------------
+        /**
+         * public function getCacheData() - read cachedata if it exist
+         * @return     various  cachedata or false if cache does not exist
+         */
+        public function read()
+        {
+            if (!isset($this->_aCacheInfos['data'])) {
+                $this->_getAllCacheData();
+            }
+            if (!isset($this->_aCacheInfos['data'])) {
+                return false;
+            }
+            return $this->_aCacheInfos['data'];
+        }
+
+        // ----------------------------------------------------------------------
+        /**
+         * delete module based remove file
+         *
+         * @since 2.6
+         * @return boolean
+         */
+        public function removefileDelete()
+        {
+            if (file_exists($this->_sCacheRemovefile)) {
+                return unlink($this->_sCacheRemovefile);
+            }
+            return false;
+        }
+        // ----------------------------------------------------------------------
+        /**
+         * make all cache items invalid by touching the remove file
+         *
+         * @since 2.6
+         * @return boolean
+         */
+        public function removefileTouch()
+        {
+            return touch($this->_sCacheRemovefile);
+        }
+        // ----------------------------------------------------------------------
+        /**
+         * public function setData($data) - set cachedata into cache object
+         * data can be any serializable type, like string, array or object
+         * Remark: You additionally need to call the write() method to store data in the filesystem
+         * @since 2.0
+         * @param      various  $data  data to store in cache
+         * @return   boolean
+         */
+        public function setData($data)
+        {
+            return $this->_aCacheInfos['data'] = $data;
+        }
+
+        /**
+         * set new cache id; it keeps current module
+         * @param  string  $sCacheID  cache-id (must be uniq within a module; used to generate filename of cachefile)
+         *                            adding the cache id is recommendet, otherwise
+         *                            you addiionally need to call setCacheId()
+         * @return boolean
+         */
+        public function setCacheId($sCacheID)
+        {
+            $this->sCacheID = $sCacheID;
 
-        if (!$data === false)
-            $this->setData($data);
+            $this->_setup();
 
-        if (!$iTtl >= 0) {
-            $this->setTtl($iTtl);
+            $this->_getCacheFilename();
+            $this->read();
+
+            return true;
+        }
+        /**
+         * set module
+         * @param  string  $sModule   name of module or app that uses the cache
+         * @param  string  $sCacheID  optional: cache-id (must be uniq within a module; used to generate filename of cachefile)
+         *                            adding the cache id is recommendet, otherwise
+         *                            you addiionally need to call setCacheId()
+         * @return boolean
+         */
+        public function setModule($sModule, $sCacheID = false)
+        {
+            $this->sModule = $sModule;
+            return $this->setCacheId($sCacheID);
+        }
+        // ----------------------------------------------------------------------
+        /**
+         * public function setRefFile() - set a reference file that invalidates the
+         * cache if the file is newer than the stored item
+         * @since 2.8
+         * @param   int  $iTtl  ttl value in seconds
+         * @return  int  get ttl of cache
+         */
+        public function setRefFile($sFile)
+        {
+            return $this->_sRefFile = $sFile;
+        }
+        // ----------------------------------------------------------------------
+        /**
+         * public function setTtl() - set TTL of cache in seconds
+         * You need to write the cache data to ap
+         * Remark: You additionally need to call the write() method to store a new ttl value with 
+         * data in the filesystem
+         * @since 2.0
+         * @param   int  $iTtl  ttl value in seconds
+         * @return  int  get ttl of cache
+         */
+        public function setTtl($iTtl)
+        {
+            return $this->_iTtl = $iTtl;
         }
 
-        $aTmp = array(
-            'iTtl' => $this->_iTtl,
-            'tsExpire' => date("U") + $this->_iTtl,
-            'module' => $this->sModule,
-            'cacheid' => $this->sCacheID,
-            'data' => $this->_aCacheInfos['data'],
-        );
-        return file_put_contents($this->_sCacheFile, serialize($aTmp));
-    }
+        // ----------------------------------------------------------------------
+        /**
+         * public function touch() - touch cachefile if it exist
+         * For cached data a new expiration based on existing ttl will be set
+         * @return boolean 
+         */
+        public function touch()
+        {
+            if (!file_exists($this->_sCacheFile)) {
+                return false;
+            }
 
+            // touch der Datei reicht nicht mehr, weil tsExpire verloren ginge
+            if (!$this->_iTtl) {
+                $bReturn = touch($this->_sCacheFile);
+            } else {
+                $bReturn = $this->write();
+            }
+
+            $this->_getAllCacheData();
+
+            return $bReturn;
+        }
+
+        // ----------------------------------------------------------------------
+        /**
+         * Write data into a cache. 
+         * - data can be any serializable type, like string, array or object
+         * - set ttl in s (from now); optional parameter
+         * @param      various  $data      data to store in cache
+         * @param      int      $iTtl      optional: time in s if content cache expires (min. 0)
+         * @param      string   $sRefFile  optional: set a reference file that invalidates the cache if it is newer
+         * @return     bool     success of write action
+         */
+        public function write($data = false, $iTtl = null, $sRefFile = null)
+        {
+            if (!$this->_sCacheFile) {
+                return false;
+            }
+            $sDir = dirname($this->_sCacheFile);
+            if (!is_dir($sDir)) {
+                if (!mkdir($sDir, 0750, true)) {
+                    die("ERROR: unable to create directory " . $sDir);
+                }
+            }
+
+            if (!$data === false) {
+                $this->setData($data);
+            }
+
+            if (!is_null($iTtl)) {
+                $this->setTtl($iTtl);
+            }
+            if (!is_null($sRefFile)) {
+                $this->setRefFile($sRefFile);
+            }
+
+            $aTmp = [
+                'iTtl' => $this->_iTtl,
+                'sRefFile' => $this->_sRefFile,
+                'tsExpire' => date("U") + $this->_iTtl,
+                'module' => $this->sModule,
+                'cacheid' => $this->sCacheID,
+                'data' => isset($this->_aCacheInfos['data']) ? $this->_aCacheInfos['data'] : '',
+            ];
+            return file_put_contents($this->_sCacheFile, serialize($aTmp));
+        }
+    }
 }
 
 // ----------------------------------------------------------------------