<?php require_once 'user.class.php'; /** * class to log all project actions, ie. build, deploy etc. * * @author hahn */ class Actionlog { private $_aLoglevels = array("info", "warning", "error", "success"); // array of valid loglevels private $_sIP = false; private $_sUser = false; private $_sProject = false; private $_sCreate = ' CREATE TABLE "logs" ( `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE , `time` DATETIME, `loglevel` TEXT, `ip` TEXT, `user` TEXT, `project` TEXT, `action` TEXT, `message` TEXT )'; /* private $_sCreateFUTURE = ' CREATE TABLE "logs" ( `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE , `time` DATETIME, `time-start` DATETIME, `loglevel` TEXT, `ip` TEXT, `user` TEXT, `project` TEXT, `action` TEXT, `message` TEXT )'; */ /** * 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) { global $aConfig; if (!is_array($aConfig) || !array_key_exists("appRootDir", $aConfig)) { die(__CLASS__ . "::".__FUNCTION__." ERROR: configuration with \$aConfig was not loaded."); } $this->_dbfile = $aConfig['dataDir'] . '/database/logs.db'; if (!file_exists($this->_dbfile)) { $this->_createDb(); if (!file_exists($this->_dbfile)) { die("ERROR: unable to create sqlite database " . $this->_dbfile); } } $this->_sProject = $sProject; $oUser=new user(); if ($oUser->getUsername()) { $this->_sIP = $_SERVER["REMOTE_ADDR"]; $this->_sUser = $oUser->getUsername() . " (web)"; } else { $this->_sIP = 'local'; $aUser=posix_getpwuid(posix_geteuid()); $this->_sUser = $aUser['name'] . " (system)"; } } /** * create sqlite database - called in constructor if the file does not exist */ private function _createDb() { echo "try to create file $this->_dbfile ...<br>\n"; return $this->_makeQuery($this->_sCreate); } /** * execute a sql statement * @param string $sSql sql statement * @return database object */ private function _makeQuery($sSql) { // $this->_log(__FUNCTION__."($sSql)"); // echo "<pre>$sSql</pre>"; $db = new PDO("sqlite:" . $this->_dbfile); $result = $db->query($sSql); /* if(!$result){ echo "PDO ERROR " . print_r($db->errorInfo(), 1) ."<br>"; } */ $db = NULL; return $result; } /** * add a log message * @param string $sMessage message * @param string $sAction project action; i.e. build, deploy, ... * @param string $sLoglevel loglevel * @return type */ public function add($sMessage, $sAction = "", $sLoglevel = "info", $sTimeStart = false) { if (array_search($sLoglevel, $this->_aLoglevels) === false) { die(__class__ . ": loglevel $sLoglevel is invalid"); } $sql = "INSERT INTO `logs` (`time`, `loglevel`, `ip`, `user`, `project` ,`action`, `message`) VALUES( '" . date("Y-m-d H:i:s") . "', '" . $sLoglevel . "', '" . $this->_sIP . "', '" . $this->_sUser . "', '" . $this->_sProject . "', '" . $sAction . "', '" . $sMessage . "' ); "; /* $sqlFUTURE = "INSERT INTO `logs` (`time`, ".($sTimeStart ? "`time-start`, " : "" )." `loglevel`, `ip`, `user`, `project` ,`action`, `message`) VALUES( '" . date("Y-m-d H:i:s") . "', ".($sTimeStart ? "'" . $sTimeStart . "', " : "" ) ." '" . $sLoglevel . "', '" . $this->_sIP . "', '" . $this->_sUser . "', '" . $this->_sProject . "', '" . $sAction . "', '" . $sMessage . "' ); "; * */ // echo $sql . "<br>"; $oResult=$this->_makeQuery($sql); return $oResult; } /** * 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){ return preg_replace('/[^'.$sOKChars. ']/i', '',$sVal); } /** * 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" * 'to' - max time (see from) * 'order' - order clausel - part behind "ORDER BY "; default is "id DESC" (order by newest entries) * 'limit' - limit clausel - part behind "LIMIT " * @return array */ public function getLogs($aFilter = array()) { // var_dump(R::findAll( 'log' )); $aReturn = array(); $sSql = 'SELECT `id`,`time`,`loglevel`,`ip`,`user`,`project`,`action`,`message` from logs '; $sWhere = false; $aWhere=array(); if (array_key_exists("project", $aFilter) && $aFilter["project"]) { $aWhere[]='`project`="' . $this->_filterAllowedChars($aFilter["project"], '[a-z0-9\-\_]') . '"'; } if (array_key_exists("from", $aFilter) && $aFilter["from"]) { $aWhere[]='`time`>="' . $this->_filterAllowedChars($aFilter["from"], '[0-9\-\ \:]') . '"'; } if (array_key_exists("to", $aFilter) && $aFilter["to"]) { $aWhere[]='`time`<="' . $this->_filterAllowedChars($aFilter["to"], '[0-9\-\ \:]') . '"'; } $sSql.=(count($aWhere) ? 'WHERE '. implode(' AND ', $aWhere) : ''); if (array_key_exists("order", $aFilter) && $aFilter["order"]) { $sSql.=' ORDER BY ' . $this->_filterAllowedChars($aFilter["order"], '[a-z\`0-9\,\ ]'); } else { $sSql.=' ORDER BY id DESC '; } if (array_key_exists("limit", $aFilter) && $aFilter["limit"]) { $sSql.=' LIMIT ' . $this->_filterAllowedChars($aFilter["limit"], '[0-9\,\ ]'); } foreach ($this->_makeQuery($sSql) as $row) { for ($i = 0; $i <= 7; $i++) { unset($row[$i]); } $aReturn[] = $row; } return $aReturn; } /** * render 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) { $sReturn = ''; static $bWasShown; if ($bWasShown) return " "; $bWasShown = true; require_once 'formgen.class.php'; // 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( 'method' => 'GET', 'action' => '?', 'class' => 'form-inline', ), 'validate' => array(), 'form' => array(), )); // generate filter for log table // $bIsFullsearch: true = show all inputs; false: no interactive search if ($bIsFullsearch){ $aForms["filter"]["form"]['label'] = array( 'type' => 'markup', 'value' => '<h3>' . t("class-actionlog-title") . ' :: '.t("class-actionlog-filter").'</h3>', ); // --- list of all projects in log $sSql='SELECT DISTINCT(project) from `logs` order by project asc'; $aForms["filter"]["form"]['selectproject'] = array( 'type' => 'select', 'name' => 'selectproject', 'label' => '<i class="glyphicon glyphicon-tag"></i> ' . t('project'), 'class' => 'span2', 'onchange'=>'updateActionlog();', 'inline' => true, ); $aForms["filter"]["form"]['selectproject']['options']['']=array('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"]['selectfrom'] = array( 'type' => 'select', 'name' => 'selectWheretime', 'label' => '<i class="glyphicon glyphicon-calendar"></i> '.t("class-actionlog-time"), 'class' => 'span2', 'onchange'=>'updateActionlog();', 'options' => $aTimes, 'inline' => true, ); $aForms["filter"]["form"]['selectlimit'] = array( 'type' => 'select', 'name' => 'selectlimit', 'label' => '<i class="glyphicon glyphicon-list"></i> '.t("class-actionlog-count"), 'class' => 'span1', 'onchange'=>'updateActionlog();', 'options' => $aLimits, 'inline' => true, ); $aForms["filter"]["form"]['line'] = array( 'type' => 'markup', 'value' => '<hr>', ); } else { // write filters as hidden fields if (array_key_exists("project", $aFilter)){ $aForms["filter"]["form"]['selectproject'] = array( 'type' => 'hidden', 'value' => $aFilter["project"] ); } if (array_key_exists("limit", $aFilter)){ $aForms["filter"]["form"]['selectlimit'] = array( 'type' => 'hidden', 'value' => $aFilter["limit"] ); } } $aForms["filter"]["form"]['efilterlogs'] = array( 'type' => 'text', 'name' => 'efilterlogs', 'label' => '<i class="glyphicon glyphicon-filter"></i>' . t("overview-textsearch"), 'inline'=> true, 'onkeyup' => 'filterLogTable();', 'onkeypress' => 'filterLogTable();', 'onchange' => 'filterLogTable();', 'size' => 20, ); if (!$bIsFullsearch){ $aForms["filter"]["form"]['btnalllogs'] = array( 'type' => 'button', 'value' => t('show all'), 'href'=>'/deployment/all/setup/actionlog/', 'onclick'=>'location.href=\'/deployment/all/setup/actionlog/\'; return false;', ); } // generate html output $oForm = new formgen($aForms); $sReturn = '<div id="divActionlogs">'.$oForm->renderHtml("filter").' <div style="clear: both; margin-bottom: 1em;"></div> <div id="tableLogactions"></div> <!-- <script src="/vendor/vis/4.21.0/vis.min.js"></script> <link href="/vendor/vis/4.21.0/vis.min.css" rel="stylesheet" type="text/css" /> <div id="divTimeline"></div> --> </div> <script> var sMsgNolog="'.t("class-actionlog-nolog").'"; </script>'; if ($bIsFullsearch){ $sReturn.='<script> $(document).ready(function() { updateActionlog(); });</script>'; } else { $sReturn= '<strong>' . '<button onclick="setLogVisibility(\'block\');" id="btnShowLogs" class="btn btn-default btnLogs"><b class="glyphicon glyphicon-chevron-right"></b> </button>' . '<button onclick="setLogVisibility(\'none\');" id="btnHideLogs" class="btn btn-default btnLogs"><b class="glyphicon glyphicon-chevron-down"></b> </button>' . ' ' . t("class-actionlog-title") . '</strong>' . $sReturn.' <script> function getLogVisibility(){ var sReturn=localStorage.getItem("bActionlogsVisible"); sReturn=(sReturn=="block")?"block":"none"; return sReturn; } function setLogVisibility(sVisibility){ localStorage.setItem("bActionlogsVisible", sVisibility); $("#divActionlogs").css("display", sVisibility); $(".btnLogs").css("display", "none"); if (sVisibility=="block"){ $("#btnHideLogs").css("display", "inline"); $(document).ready(function() { updateActionlog(); }); } else { $("#btnShowLogs").css("display", "inline"); } } setLogVisibility(getLogVisibility()); </script>'; } return $sReturn; } }