Select Git revision
actionlog.class.php
actionlog.class.php 15.01 KiB
<?php
require_once 'user.class.php';
/**
* class to log all project actions, ie. build, deploy etc.
*
* @author hahn
*/
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 = '
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 (!isset($aConfig["appRootDir"])) {
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 = isset($_SERVER["REMOTE_ADDR"]) ? $_SERVER["REMOTE_ADDR"] : 'local';
$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>".htmlentities($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 . "',
'" . str_replace("'", '"', $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 (isset($aFilter["project"]) && $aFilter["project"]) {
$aWhere[] = '`project`="' . $this->_filterAllowedChars($aFilter["project"], '[a-z0-9\-\_]') . '"';
}
if (isset($aFilter["from"]) && $aFilter["from"]) {
$aWhere[] = '`time`>="' . $this->_filterAllowedChars($aFilter["from"], '[0-9\-\ \:]') . '"';
}
if (isset($aFilter["to"]) && $aFilter["to"]) {
$aWhere[] = '`time`<="' . $this->_filterAllowedChars($aFilter["to"], '[0-9\-\ \:]') . '"';
}
$sSql .= (count($aWhere) ? 'WHERE ' . implode(' AND ', $aWhere) : '');
if (isset($aFilter["order"]) && $aFilter["order"]) {
$sSql .= ' ORDER BY ' . $this->_filterAllowedChars($aFilter["order"], '[a-z\`0-9\,\ ]');
} else {
$sSql .= ' ORDER BY id DESC ';
}
if (isset($aFilter["limit"]) && $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) {
// --- 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,
);
// force a line break
$aForms["filter"]["form"]['line'] = array(
'type' => 'markup',
'value' => '<div class="col-sm-12"></div>',
);
} else {
// write filters as hidden fields
if (isset($aFilter["project"])) {
$aForms["filter"]["form"]['selectproject'] = array(
'type' => 'hidden',
'value' => $aFilter["project"]
);
}
if (isset($aFilter["limit"])) {
$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'),
'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(
// 'type' => 'markup',
// 'value' => '</div><!-- closediv -->',
// );
}
// generate html output
$oForm = new formgen($aForms);
$sReturn = ''
. ($bIsFullsearch ? t("class-actionlog-title") . ' :: ' . t("class-actionlog-filter") : '')
. '<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"><i class="fa-solid fa-chevron-right"></i> </button>'
. '<button onclick="setLogVisibility(\'none\');" id="btnHideLogs" class="btn btn-default btnLogs"><i class="fa-solid fa-chevron-down"></i> </button>'
. ' ' . t("class-actionlog-title") . (isset($aFilter["project"]) ? ' [' . $aFilter["project"] . '] ' : '')
. '</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;
}
}