Merge branch 'master' of github.com:ZoneMinder/zoneminder
This commit is contained in:
commit
5b27d77ea4
|
@ -1,28 +1,107 @@
|
||||||
<?php
|
<?php
|
||||||
ini_set('display_errors', '0');
|
|
||||||
|
|
||||||
if ( empty($_REQUEST['eids']) ) {
|
$message = '';
|
||||||
ajaxError('No event id(s) supplied');
|
$data = array();
|
||||||
|
|
||||||
|
//
|
||||||
|
// INITIALIZE AND CHECK SANITY
|
||||||
|
//
|
||||||
|
|
||||||
|
if ( !canEdit('Events') ) $message = 'Insufficient permissions for user '.$user['Username'];
|
||||||
|
|
||||||
|
if ( empty($_REQUEST['task']) ) {
|
||||||
|
$message = 'Must specify a task';
|
||||||
|
} else {
|
||||||
|
$task = $_REQUEST['task'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( canView('Events') ) {
|
if ( empty($_REQUEST['eids']) ) {
|
||||||
} // end if canView('Events')
|
if ( isset($_REQUEST['task']) && $_REQUEST['task'] != "query" ) $message = 'No event id(s) supplied';
|
||||||
|
} else {
|
||||||
|
$eids = $_REQUEST['eids'];
|
||||||
|
}
|
||||||
|
|
||||||
if ( canEdit('Events') ) {
|
if ( $message ) {
|
||||||
$message = array();
|
ajaxError($message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
foreach ( $_REQUEST['eids'] as $eid ) {
|
// Search contains a user entered string to search on
|
||||||
|
$search = isset($_REQUEST['search']) ? $_REQUEST['search'] : '';
|
||||||
|
|
||||||
switch ( $_REQUEST['action'] ) {
|
// Advanced search contains an array of "column name" => "search text" pairs
|
||||||
|
// Bootstrap table sends json_ecoded array, which we must decode
|
||||||
|
$advsearch = isset($_REQUEST['filter']) ? json_decode($_REQUEST['filter'], JSON_OBJECT_AS_ARRAY) : array();
|
||||||
|
|
||||||
|
// Sort specifies the name of the column to sort on
|
||||||
|
$sort = 'StartTime';
|
||||||
|
if ( isset($_REQUEST['sort']) ) {
|
||||||
|
if ( !in_array($_REQUEST['sort'], array_merge($columns, $col_alt)) ) {
|
||||||
|
ZM\Error('Invalid sort field: ' . $_REQUEST['sort']);
|
||||||
|
} else {
|
||||||
|
$sort = $_REQUEST['sort'];
|
||||||
|
//if ( $sort == 'DateTime' ) $sort = 'TimeKey';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset specifies the starting row to return, used for pagination
|
||||||
|
$offset = 0;
|
||||||
|
if ( isset($_REQUEST['offset']) ) {
|
||||||
|
if ( ( !is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']) ) ) {
|
||||||
|
ZM\Error('Invalid value for offset: ' . $_REQUEST['offset']);
|
||||||
|
} else {
|
||||||
|
$offset = $_REQUEST['offset'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Order specifies the sort direction, either asc or desc
|
||||||
|
$order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc')) ? 'ASC' : 'DESC';
|
||||||
|
|
||||||
|
// Limit specifies the number of rows to return
|
||||||
|
$limit = 100;
|
||||||
|
if ( isset($_REQUEST['limit']) ) {
|
||||||
|
if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) {
|
||||||
|
ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']);
|
||||||
|
} else {
|
||||||
|
$limit = $_REQUEST['limit'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// MAIN LOOP
|
||||||
|
//
|
||||||
|
|
||||||
|
switch ( $task ) {
|
||||||
case 'archive' :
|
case 'archive' :
|
||||||
case 'unarchive' :
|
case 'unarchive' :
|
||||||
$archiveVal = ($_REQUEST['action'] == 'archive') ? 1 : 0;
|
foreach ( $eids as $eid ) archiveRequest($task, $eid);
|
||||||
|
break;
|
||||||
|
case 'delete' :
|
||||||
|
foreach ( $eids as $eid ) $data[] = deleteRequest($eid);
|
||||||
|
break;
|
||||||
|
case 'query' :
|
||||||
|
$data = queryRequest($search, $advsearch, $sort, $offset, $order, $limit);
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
ZM\Fatal("Unrecognised task '$task'");
|
||||||
|
} // end switch task
|
||||||
|
|
||||||
|
ajaxResponse($data);
|
||||||
|
|
||||||
|
//
|
||||||
|
// FUNCTION DEFINITIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
function archiveRequest($task, $eid) {
|
||||||
|
$archiveVal = ($task == 'archive') ? 1 : 0;
|
||||||
dbQuery(
|
dbQuery(
|
||||||
'UPDATE Events SET Archived = ? WHERE Id = ?',
|
'UPDATE Events SET Archived = ? WHERE Id = ?',
|
||||||
array($archiveVal, $eid)
|
array($archiveVal, $eid)
|
||||||
);
|
);
|
||||||
break;
|
}
|
||||||
case 'delete' :
|
|
||||||
|
function deleteRequest($eid) {
|
||||||
|
$message = array();
|
||||||
$event = new ZM\Event($eid);
|
$event = new ZM\Event($eid);
|
||||||
if ( !$event->Id() ) {
|
if ( !$event->Id() ) {
|
||||||
$message[] = array($eid=>'Event not found.');
|
$message[] = array($eid=>'Event not found.');
|
||||||
|
@ -31,11 +110,98 @@ if ( canEdit('Events') ) {
|
||||||
} else {
|
} else {
|
||||||
$event->delete();
|
$event->delete();
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
} // end switch action
|
|
||||||
} // end foreach
|
|
||||||
ajaxResponse($message);
|
|
||||||
} // end if canEdit('Events')
|
|
||||||
|
|
||||||
ajaxError('Unrecognised action '.$_REQUEST['action'].' or insufficient permissions for user '.$user['Username']);
|
return $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
function queryRequest($search, $advsearch, $sort, $offset, $order, $limit) {
|
||||||
|
// Put server pagination code here
|
||||||
|
// The table we want our data from
|
||||||
|
$table = 'Events';
|
||||||
|
|
||||||
|
// The names of the dB columns in the log table we are interested in
|
||||||
|
$columns = array('Id', 'MonitorId', 'StorageId', 'Name', 'Cause', 'StartTime', 'EndTime', 'Length', 'Frames', 'AlarmFrames', 'TotScore', 'AvgScore', 'MaxScore', 'Archived', 'Emailed', 'Notes', 'DiskSpace');
|
||||||
|
|
||||||
|
// The names of columns shown in the log view that are NOT dB columns in the database
|
||||||
|
$col_alt = array('Monitor', 'Storage');
|
||||||
|
|
||||||
|
$col_str = implode(', ', $columns);
|
||||||
|
$data = array();
|
||||||
|
$query = array();
|
||||||
|
$query['values'] = array();
|
||||||
|
$likes = array();
|
||||||
|
$where = '';
|
||||||
|
// There are two search bars in the log view, normal and advanced
|
||||||
|
// Making an exuctive decision to ignore the normal search, when advanced search is in use
|
||||||
|
// Alternatively we could try to do both
|
||||||
|
if ( count($advsearch) ) {
|
||||||
|
|
||||||
|
foreach ( $advsearch as $col=>$text ) {
|
||||||
|
if ( !in_array($col, array_merge($columns, $col_alt)) ) {
|
||||||
|
ZM\Error("'$col' is not a sortable column name");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$text = '%' .$text. '%';
|
||||||
|
array_push($likes, $col.' LIKE ?');
|
||||||
|
array_push($query['values'], $text);
|
||||||
|
}
|
||||||
|
$wherevalues = $query['values'];
|
||||||
|
$where = ' WHERE (' .implode(' OR ', $likes). ')';
|
||||||
|
|
||||||
|
} else if ( $search != '' ) {
|
||||||
|
|
||||||
|
$search = '%' .$search. '%';
|
||||||
|
foreach ( $columns as $col ) {
|
||||||
|
array_push($likes, $col.' LIKE ?');
|
||||||
|
array_push($query['values'], $search);
|
||||||
|
}
|
||||||
|
$wherevalues = $query['values'];
|
||||||
|
$where = ' WHERE (' .implode(' OR ', $likes). ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
$query['sql'] = 'SELECT ' .$col_str. ' FROM `' .$table. '` ' .$where. ' ORDER BY ' .$sort. ' ' .$order. ' LIMIT ?, ?';
|
||||||
|
array_push($query['values'], $offset, $limit);
|
||||||
|
|
||||||
|
//ZM\Warning('Calling the following sql query: ' .$query['sql']);
|
||||||
|
|
||||||
|
$data['totalNotFiltered'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table, 'Total');
|
||||||
|
if ( $search != '' || count($advsearch) ) {
|
||||||
|
$data['total'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table.$where , 'Total', $wherevalues);
|
||||||
|
} else {
|
||||||
|
$data['total'] = $data['totalNotFiltered'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$storage_areas = ZM\Storage::find();
|
||||||
|
$StorageById = array();
|
||||||
|
foreach ( $storage_areas as $S ) {
|
||||||
|
$StorageById[$S->Id()] = $S;
|
||||||
|
}
|
||||||
|
|
||||||
|
$monitor_names = ZM\Monitor::find();
|
||||||
|
$MonitorById = array();
|
||||||
|
foreach ( $monitor_names as $S ) {
|
||||||
|
$MonitorById[$S->Id()] = $S;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows = array();
|
||||||
|
foreach ( dbFetchAll($query['sql'], NULL, $query['values']) as $row ) {
|
||||||
|
// Modify the row data as needed
|
||||||
|
$row['Name'] = validHtmlStr($row['Name']);
|
||||||
|
$row['Archived'] = $row['Archived'] ? translate('Yes') : translate('No');
|
||||||
|
$row['Emailed'] = $row['Emailed'] ? translate('Yes') : translate('No');
|
||||||
|
$row['Monitor'] = ( $row['MonitorId'] and isset($MonitorById[$row['MonitorId']]) ) ? $MonitorById[$row['MonitorId']]->Name() : '';
|
||||||
|
$row['Cause'] = validHtmlStr($row['Cause']);
|
||||||
|
$row['StartTime'] = strftime(STRF_FMT_DATETIME_SHORTER, strtotime($row['StartTime']));
|
||||||
|
$row['EndTime'] = strftime(STRF_FMT_DATETIME_SHORTER, strtotime($row['StartTime']));
|
||||||
|
$row['Length'] = gmdate('H:i:s', $row['Length'] );
|
||||||
|
$row['Storage'] = ( $row['StorageId'] and isset($StorageById[$row['StorageId']]) ) ? $StorageById[$row['StorageId']]->Name() : 'Default';
|
||||||
|
$row['Notes'] = htmlspecialchars($row['Notes']);
|
||||||
|
$row['DiskSpace'] = human_filesize($row['DiskSpace']);
|
||||||
|
$rows[] = $row;
|
||||||
|
}
|
||||||
|
$data['rows'] = $rows;
|
||||||
|
$data['updated'] = preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
|
|
552
web/ajax/log.php
552
web/ajax/log.php
|
@ -1,466 +1,132 @@
|
||||||
<?php
|
<?php
|
||||||
ini_set('display_errors', '0');
|
global $Servers;
|
||||||
|
|
||||||
# Moved up here because it is used in several spots.
|
if ( !canView('System') ) {
|
||||||
# These are the valid columns that you can filter on.
|
ajaxError('Insufficient permissions to view log entries');
|
||||||
$filterFields = array('Component', 'ServerId', 'Pid', 'Level', 'File', 'Line');
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
function buildLogQuery($action) {
|
// Only the query task is supported at the moment
|
||||||
global $filterFields;
|
if ( !isset($_REQUEST['task']) or $_REQUEST['task'] != 'query' ) {
|
||||||
|
ajaxError('Unrecognised action');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// The table we want our data from
|
||||||
|
$table = 'Logs';
|
||||||
|
|
||||||
$minTime = isset($_REQUEST['minTime']) ? $_REQUEST['minTime'] : NULL;
|
// The names of the dB columns in the log table we are interested in
|
||||||
$maxTime = isset($_REQUEST['maxTime']) ? $_REQUEST['maxTime'] : NULL;
|
$columns = array('TimeKey', 'Component', 'ServerId', 'Pid', 'Code', 'Message', 'File', 'Line');
|
||||||
|
|
||||||
$limit = 100;
|
// The names of columns shown in the log view that are NOT dB columns in the database
|
||||||
if ( isset($_REQUEST['limit']) ) {
|
$col_alt = array('DateTime', 'Server');
|
||||||
if ( ( !is_integer($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) {
|
|
||||||
ZM\Error('Invalid value for limit ' . $_REQUEST['limit']);
|
// Search contains a user entered string to search on
|
||||||
|
$search = isset($_REQUEST['search']) ? $_REQUEST['search'] : '';
|
||||||
|
|
||||||
|
// Advanced search contains an array of "column name" => "search text" pairs
|
||||||
|
// Bootstrap table sends json_ecoded array, which we must decode
|
||||||
|
$advsearch = isset($_REQUEST['filter']) ? json_decode($_REQUEST['filter'], JSON_OBJECT_AS_ARRAY) : array();
|
||||||
|
|
||||||
|
// Sort specifies the name of the column to sort on
|
||||||
|
$sort = 'TimeKey';
|
||||||
|
if ( isset($_REQUEST['sort']) ) {
|
||||||
|
if ( !in_array($_REQUEST['sort'], array_merge($columns, $col_alt)) ) {
|
||||||
|
ZM\Error('Invalid sort field: ' . $_REQUEST['sort']);
|
||||||
|
} else {
|
||||||
|
$sort = $_REQUEST['sort'];
|
||||||
|
if ( $sort == 'DateTime' ) $sort = 'TimeKey';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset specifies the starting row to return, used for pagination
|
||||||
|
$offset = 0;
|
||||||
|
if ( isset($_REQUEST['offset']) ) {
|
||||||
|
if ( ( !is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']) ) ) {
|
||||||
|
ZM\Error('Invalid value for offset: ' . $_REQUEST['offset']);
|
||||||
|
} else {
|
||||||
|
$offset = $_REQUEST['offset'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Order specifies the sort direction, either asc or desc
|
||||||
|
$order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc')) ? 'ASC' : 'DESC';
|
||||||
|
|
||||||
|
// Limit specifies the number of rows to return
|
||||||
|
$limit = 100;
|
||||||
|
if ( isset($_REQUEST['limit']) ) {
|
||||||
|
if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) {
|
||||||
|
ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']);
|
||||||
} else {
|
} else {
|
||||||
$limit = $_REQUEST['limit'];
|
$limit = $_REQUEST['limit'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$sortField = 'TimeKey';
|
|
||||||
if ( isset($_REQUEST['sortField']) ) {
|
|
||||||
if ( !in_array($_REQUEST['sortField'], $filterFields) and ( $_REQUEST['sortField'] != 'TimeKey' ) ) {
|
|
||||||
ZM\Error('Invalid sort field ' . $_REQUEST['sortField']);
|
|
||||||
} else {
|
|
||||||
$sortField = $_REQUEST['sortField'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$sortOrder = (isset($_REQUEST['sortOrder']) and ($_REQUEST['sortOrder'] == 'asc')) ? 'asc' : 'desc';
|
|
||||||
$filter = isset($_REQUEST['filter']) ? $_REQUEST['filter'] : array();
|
|
||||||
|
|
||||||
$sql = $action.' FROM Logs';
|
$col_str = implode(', ', $columns);
|
||||||
$where = array();
|
$data = array();
|
||||||
$values = array();
|
$query = array();
|
||||||
if ( $minTime ) {
|
$query['values'] = array();
|
||||||
$where[] = 'TimeKey > ?';
|
$likes = array();
|
||||||
$values[] = $minTime;
|
$where = '';
|
||||||
} elseif ( $maxTime ) {
|
// There are two search bars in the log view, normal and advanced
|
||||||
$where[] = 'TimeKey < ?';
|
// Making an exuctive decision to ignore the normal search, when advanced search is in use
|
||||||
$values[] = $maxTime;
|
// Alternatively we could try to do both
|
||||||
}
|
if ( count($advsearch) ) {
|
||||||
|
|
||||||
foreach ( $filter as $field=>$value ) {
|
foreach ( $advsearch as $col=>$text ) {
|
||||||
if ( !in_array($field, $filterFields) ) {
|
if ( !in_array($col, array_merge($columns, $col_alt)) ) {
|
||||||
ZM\Error("'$field' is not in valid filter fields " . print_r($filterField, true));
|
ZM\Error("'$col' is not a sortable column name");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ( $field == 'Level' ) {
|
$text = '%' .$text. '%';
|
||||||
$where[] = $field.' <= ?';
|
array_push($likes, $col.' LIKE ?');
|
||||||
$values[] = $value;
|
array_push($query['values'], $text);
|
||||||
} else {
|
|
||||||
$where[] = $field.' = ?';
|
|
||||||
$values[] = $value;
|
|
||||||
}
|
}
|
||||||
|
$wherevalues = $query['values'];
|
||||||
|
$where = ' WHERE (' .implode(' OR ', $likes). ')';
|
||||||
|
|
||||||
|
} else if ( $search != '' ) {
|
||||||
|
|
||||||
|
$search = '%' .$search. '%';
|
||||||
|
foreach ( $columns as $col ) {
|
||||||
|
array_push($likes, $col.' LIKE ?');
|
||||||
|
array_push($query['values'], $search);
|
||||||
}
|
}
|
||||||
if ( count($where) )
|
$wherevalues = $query['values'];
|
||||||
$sql.= ' WHERE '.join(' AND ', $where);
|
$where = ' WHERE (' .implode(' OR ', $likes). ')';
|
||||||
$sql .= ' ORDER BY '.$sortField.' '.$sortOrder.' LIMIT '.$limit;
|
}
|
||||||
|
|
||||||
return array('sql'=>$sql, 'values'=>$values);
|
$query['sql'] = 'SELECT ' .$col_str. ' FROM `' .$table. '` ' .$where. ' ORDER BY ' .$sort. ' ' .$order. ' LIMIT ?, ?';
|
||||||
} # function buildLogQuery($action)
|
array_push($query['values'], $offset, $limit);
|
||||||
|
|
||||||
switch ( $_REQUEST['task'] ) {
|
//ZM\Warning('Calling the following sql query: ' .$query['sql']);
|
||||||
case 'create' :
|
|
||||||
{
|
|
||||||
// Silently ignore bogus requests
|
|
||||||
if ( !empty($_POST['level']) && !empty($_POST['message']) ) {
|
|
||||||
ZM\logInit(array('id'=>'web_js'));
|
|
||||||
|
|
||||||
$string = $_POST['message'];
|
$data['totalNotFiltered'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table, 'Total');
|
||||||
|
if ( $search != '' || count($advsearch) ) {
|
||||||
|
$data['total'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table.$where , 'Total', $wherevalues);
|
||||||
|
} else {
|
||||||
|
$data['total'] = $data['totalNotFiltered'];
|
||||||
|
}
|
||||||
|
|
||||||
$file = !empty($_POST['file']) ? preg_replace('/\w+:\/\/[\w.:]+\//', '', $_POST['file']) : '';
|
if ( !$Servers )
|
||||||
if ( !empty($_POST['line']) ) {
|
|
||||||
$line = validInt($_POST['line']);
|
|
||||||
} else {
|
|
||||||
$line = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
$levels = array_flip(ZM\Logger::$codes);
|
|
||||||
if ( !isset($levels[$_POST['level']]) ) {
|
|
||||||
ZM\Panic('Unexpected logger level '.$_POST['level']);
|
|
||||||
}
|
|
||||||
$level = $levels[$_POST['level']];
|
|
||||||
ZM\Logger::fetch()->logPrint($level, $string, $file, $line);
|
|
||||||
} else {
|
|
||||||
ZM\Error('Invalid log create: '.print_r($_POST, true));
|
|
||||||
}
|
|
||||||
ajaxResponse();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'delete' :
|
|
||||||
{
|
|
||||||
if ( !canEdit('System') )
|
|
||||||
ajaxError('Insufficient permissions to delete log entries');
|
|
||||||
|
|
||||||
$query = buildLogQuery('DELETE');
|
|
||||||
$result = dbQuery($query['sql'], $query['values']);
|
|
||||||
ajaxResponse(array('result'=>'Ok', 'deleted'=>$result->rowCount()));
|
|
||||||
}
|
|
||||||
case 'query' :
|
|
||||||
{
|
|
||||||
if ( !canView('System') )
|
|
||||||
ajaxError('Insufficient permissions to view log entries');
|
|
||||||
$total = dbFetchOne('SELECT count(*) AS Total FROM Logs', 'Total');
|
|
||||||
$query = buildLogQuery('SELECT *');
|
|
||||||
|
|
||||||
global $Servers;
|
|
||||||
if ( !$Servers )
|
|
||||||
$Servers = ZM\Server::find();
|
$Servers = ZM\Server::find();
|
||||||
$servers_by_Id = array();
|
$servers_by_Id = array();
|
||||||
# There is probably a better way to do this.
|
# There is probably a better way to do this.
|
||||||
foreach ( $Servers as $server ) {
|
foreach ( $Servers as $server ) {
|
||||||
$servers_by_Id[$server->Id()] = $server;
|
$servers_by_Id[$server->Id()] = $server;
|
||||||
}
|
}
|
||||||
|
|
||||||
$logs = array();
|
$rows = array();
|
||||||
$options = array();
|
foreach ( dbFetchAll($query['sql'], NULL, $query['values']) as $row ) {
|
||||||
|
$row['DateTime'] = strftime('%Y-%m-%d %H:%M:%S', intval($row['TimeKey']));
|
||||||
|
$row['Server'] = ( $row['ServerId'] and isset($servers_by_Id[$row['ServerId']]) ) ? $servers_by_Id[$row['ServerId']]->Name() : '';
|
||||||
|
// First strip out any html tags
|
||||||
|
// Second strip out all characters that are not ASCII 32-126 (yes, 126)
|
||||||
|
$row['Message'] = preg_replace('/[^\x20-\x7E]/', '', strip_tags($row['Message']));
|
||||||
|
$rows[] = $row;
|
||||||
|
}
|
||||||
|
$data['rows'] = $rows;
|
||||||
|
$data['logstate'] = logState();
|
||||||
|
$data['updated'] = preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG);
|
||||||
|
|
||||||
foreach ( dbFetchAll($query['sql'], NULL, $query['values']) as $log ) {
|
ajaxResponse($data);
|
||||||
|
|
||||||
$log['DateTime'] = strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey']));
|
|
||||||
$log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : '';
|
|
||||||
$log['Message'] = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $log['Message']);
|
|
||||||
foreach ( $filterFields as $field ) {
|
|
||||||
if ( !isset($options[$field]) )
|
|
||||||
$options[$field] = array();
|
|
||||||
$value = $log[$field];
|
|
||||||
|
|
||||||
if ( $field == 'Level' ) {
|
|
||||||
if ( $value <= ZM\Logger::INFO )
|
|
||||||
$options[$field][$value] = ZM\Logger::$codes[$value];
|
|
||||||
else
|
|
||||||
$options[$field][$value] = 'DB'.$value;
|
|
||||||
} else if ( $field == 'ServerId' ) {
|
|
||||||
$options['ServerId'][$value] = ( $value and isset($servers_by_Id[$value]) ) ? $servers_by_Id[$value]->Name() : '';
|
|
||||||
} else if ( isset($log[$field]) ) {
|
|
||||||
$options[$field][$log[$field]] = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$logs[] = $log;
|
|
||||||
} # end foreach log db row
|
|
||||||
|
|
||||||
foreach ( $options as $field => $values ) {
|
|
||||||
asort($options[$field]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$available = count($logs);
|
|
||||||
ajaxResponse(array(
|
|
||||||
'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG),
|
|
||||||
'total' => $total,
|
|
||||||
'available' => isset($available) ? $available : $total,
|
|
||||||
'logs' => $logs,
|
|
||||||
'state' => logState(),
|
|
||||||
'options' => $options,
|
|
||||||
));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'export' :
|
|
||||||
{
|
|
||||||
if ( !canView('System') )
|
|
||||||
ajaxError('Insufficient permissions to export logs');
|
|
||||||
|
|
||||||
$minTime = isset($_POST['minTime']) ? $_POST['minTime'] : NULL;
|
|
||||||
$maxTime = isset($_POST['maxTime']) ? $_POST['maxTime'] : NULL;
|
|
||||||
if ( !is_null($minTime) && !is_null($maxTime) && ($minTime > $maxTime) ) {
|
|
||||||
$tempTime = $minTime;
|
|
||||||
$minTime = $maxTime;
|
|
||||||
$maxTime = $tempTime;
|
|
||||||
}
|
|
||||||
//$limit = isset($_POST['limit'])?$_POST['limit']:1000;
|
|
||||||
$filter = isset($_POST['filter'])?$_POST['filter']:array();
|
|
||||||
$sortField = 'TimeKey';
|
|
||||||
if ( isset($_POST['sortField']) ) {
|
|
||||||
if ( !in_array($_POST['sortField'], $filterFields) and ($_POST['sortField'] != 'TimeKey') ) {
|
|
||||||
ZM\Error('Invalid sort field '.$_POST['sortField']);
|
|
||||||
} else {
|
|
||||||
$sortField = $_POST['sortField'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$sortOrder = (isset($_POST['sortOrder']) and $_POST['sortOrder']) == 'asc' ? 'asc' : 'desc';
|
|
||||||
|
|
||||||
global $Servers;
|
|
||||||
if ( !$Servers )
|
|
||||||
$Servers = ZM\Server::find();
|
|
||||||
$servers_by_Id = array();
|
|
||||||
# There is probably a better way to do this.
|
|
||||||
foreach ( $Servers as $server ) {
|
|
||||||
$servers_by_Id[$server->Id()] = $server;
|
|
||||||
}
|
|
||||||
|
|
||||||
$sql = 'SELECT * FROM Logs';
|
|
||||||
$where = array();
|
|
||||||
$values = array();
|
|
||||||
if ( $minTime ) {
|
|
||||||
if ( preg_match('/(.+)(\.\d+)/', $minTime, $matches) ) {
|
|
||||||
# This handles sub second precision
|
|
||||||
$minTime = strtotime($matches[1]).$matches[2];
|
|
||||||
} else {
|
|
||||||
$minTime = strtotime($minTime);
|
|
||||||
}
|
|
||||||
$where[] = 'TimeKey >= ?';
|
|
||||||
$values[] = $minTime;
|
|
||||||
}
|
|
||||||
if ( $maxTime ) {
|
|
||||||
if ( preg_match('/(.+)(\.\d+)/', $maxTime, $matches) ) {
|
|
||||||
$maxTime = strtotime($matches[1]).$matches[2];
|
|
||||||
} else {
|
|
||||||
$maxTime = strtotime($maxTime);
|
|
||||||
}
|
|
||||||
$where[] = 'TimeKey <= ?';
|
|
||||||
$values[] = $maxTime;
|
|
||||||
}
|
|
||||||
foreach ( $filter as $field=>$value ) {
|
|
||||||
if ( $value != '' ) {
|
|
||||||
if ( $field == 'Level' ) {
|
|
||||||
$where[] = $field.' <= ?';
|
|
||||||
$values[] = $value;
|
|
||||||
} else {
|
|
||||||
$where[] = $field.' = ?';
|
|
||||||
$values[] = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( count($where) )
|
|
||||||
$sql.= ' WHERE '.join(' AND ', $where);
|
|
||||||
$sql .= ' ORDER BY '.$sortField.' '.$sortOrder;
|
|
||||||
//$sql .= " limit ".dbEscape($limit);
|
|
||||||
$format = isset($_POST['format']) ? $_POST['format'] : 'text';
|
|
||||||
switch ( $format ) {
|
|
||||||
case 'text' :
|
|
||||||
$exportExt = 'txt';
|
|
||||||
break;
|
|
||||||
case 'tsv' :
|
|
||||||
$exportExt = 'tsv';
|
|
||||||
break;
|
|
||||||
case 'html' :
|
|
||||||
$exportExt = 'html';
|
|
||||||
break;
|
|
||||||
case 'xml' :
|
|
||||||
$exportExt = 'xml';
|
|
||||||
break;
|
|
||||||
default :
|
|
||||||
ZM\Fatal("Unrecognised log export format '$format'");
|
|
||||||
}
|
|
||||||
$exportKey = substr(md5(rand()), 0, 8);
|
|
||||||
$exportFile = 'zm-log.'.$exportExt;
|
|
||||||
|
|
||||||
// mkdir will generate a warning if it exists, but that is ok
|
|
||||||
error_reporting(0);
|
|
||||||
if ( ! ( mkdir(ZM_DIR_EXPORTS) || file_exists(ZM_DIR_EXPORTS) ) ) {
|
|
||||||
ZM\Fatal('Can\'t create exports dir at \''.ZM_DIR_EXPORTS.'\'');
|
|
||||||
}
|
|
||||||
$exportPath = ZM_DIR_EXPORTS.'/zm-log-'.$exportKey.$exportExt;
|
|
||||||
ZM\Debug("Exporting to $exportPath");
|
|
||||||
if ( !($exportFP = fopen($exportPath, 'w')) )
|
|
||||||
ZM\Fatal("Unable to open log export file $exportPath");
|
|
||||||
$logs = array();
|
|
||||||
foreach ( dbFetchAll($sql, NULL, $values) as $log ) {
|
|
||||||
$log['DateTime'] = preg_replace('/^\d+/', strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey'])), $log['TimeKey']);
|
|
||||||
$log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : '';
|
|
||||||
$logs[] = $log;
|
|
||||||
}
|
|
||||||
ZM\Debug(count($logs).' lines being exported by '.$sql.implode(',', $values));
|
|
||||||
|
|
||||||
switch( $format ) {
|
|
||||||
case 'text' :
|
|
||||||
{
|
|
||||||
foreach ( $logs as $log ) {
|
|
||||||
if ( $log['Line'] )
|
|
||||||
fprintf($exportFP, "%s %s[%d].%s-%s/%d [%s]\n",
|
|
||||||
$log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['File'], $log['Line'], $log['Message']);
|
|
||||||
else
|
|
||||||
fprintf($exportFP, "%s %s[%d].%s-%s [%s]\n",
|
|
||||||
$log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['File'], $log['Message']);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'tsv' :
|
|
||||||
{
|
|
||||||
# This line doesn't need fprintf, it could use fwrite
|
|
||||||
fprintf($exportFP, join("\t",
|
|
||||||
translate('DateTime'),
|
|
||||||
translate('Component'),
|
|
||||||
translate('Server'),
|
|
||||||
translate('Pid'),
|
|
||||||
translate('Level'),
|
|
||||||
translate('Message'),
|
|
||||||
translate('File'),
|
|
||||||
translate('Line')
|
|
||||||
)."\n");
|
|
||||||
foreach ( $logs as $log ) {
|
|
||||||
fprintf($exportFP, "%s\t%s\t%s\t%d\t%s\t%s\t%s\t%s\n",
|
|
||||||
$log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line']);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'html' :
|
|
||||||
{
|
|
||||||
fwrite($exportFP,
|
|
||||||
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
||||||
<head>
|
|
||||||
<title>'.translate('ZoneMinderLog').'</title>
|
|
||||||
<style type="text/css">
|
|
||||||
body, h3, p, table, td {
|
|
||||||
font-family: Verdana, Arial, Helvetica, sans-serif;
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
th {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
th, td {
|
|
||||||
border: 1px solid #888888;
|
|
||||||
padding: 1px 2px;
|
|
||||||
}
|
|
||||||
tr.log-fat td {
|
|
||||||
background-color:#ffcccc;
|
|
||||||
font-weight: bold;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
tr.log-err td {
|
|
||||||
background-color:#ffcccc;
|
|
||||||
}
|
|
||||||
tr.log-war td {
|
|
||||||
background-color: #ffe4b5;
|
|
||||||
}
|
|
||||||
tr.log-dbg td {
|
|
||||||
color: #666666;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h3>'.translate('ZoneMinderLog').'</h3>
|
|
||||||
<p>'.htmlspecialchars(preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG)).'</p>
|
|
||||||
<p>'.count($logs).' '.translate('Logs').'</p>
|
|
||||||
<table>
|
|
||||||
<tbody>
|
|
||||||
<tr><th>'.translate('DateTime').'</th><th>'.translate('Component').'</th><th>'.translate('Server').'</th><th>'.translate('Pid').'</th><th>'.translate('Level').'</th><th>'.translate('Message').'</th><th>'.translate('File').'</th><th>'.translate('Line').'</th></tr>
|
|
||||||
');
|
|
||||||
foreach ( $logs as $log ) {
|
|
||||||
$classLevel = $log['Level'];
|
|
||||||
if ( $classLevel < ZM\Logger::FATAL ) {
|
|
||||||
$classLevel = ZM\Logger::FATAL;
|
|
||||||
} else if ( $classLevel > ZM\Logger::DEBUG ) {
|
|
||||||
$classLevel = ZM\Logger::DEBUG;
|
|
||||||
}
|
|
||||||
$logClass = 'log-'.strtolower(ZM\Logger::$codes[$classLevel]);
|
|
||||||
fprintf($exportFP, ' <tr class="%s"><td>%s</td><td>%s</td><td>%s</td><td>%d</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>
|
|
||||||
', $logClass, $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line']);
|
|
||||||
}
|
|
||||||
fwrite($exportFP,
|
|
||||||
' </tbody>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'xml' :
|
|
||||||
{
|
|
||||||
fwrite($exportFP,
|
|
||||||
'<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<logexport title="'.translate('ZoneMinderLog').'" date="'.htmlspecialchars(preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG)).'">
|
|
||||||
<selector>'.$_POST['selector'].'</selector>');
|
|
||||||
foreach ( $filter as $field=>$value )
|
|
||||||
if ( $value != '' )
|
|
||||||
fwrite( $exportFP,
|
|
||||||
' <filter>
|
|
||||||
<'.strtolower($field).'>'.htmlspecialchars($value).'</'.strtolower($field).'>
|
|
||||||
</filter>' );
|
|
||||||
fwrite( $exportFP,
|
|
||||||
'
|
|
||||||
<columns>
|
|
||||||
<column field="datetime">'.translate('DateTime').'</column>
|
|
||||||
<column field="component">'.translate('Component').'</column>
|
|
||||||
<column field="server">'.translate('Server').'</column>
|
|
||||||
<column field="pid">'.translate('Pid').'</column>
|
|
||||||
<column field="level">'.translate('Level').'</column>
|
|
||||||
<column field="message">'.translate('Message').'</column>
|
|
||||||
<column field="file">'.translate('File').'</column>
|
|
||||||
<column field="line">'.translate('Line').'</column>
|
|
||||||
</columns>
|
|
||||||
<logs count="'.count($logs).'">
|
|
||||||
' );
|
|
||||||
foreach ( $logs as $log ) {
|
|
||||||
fprintf( $exportFP,
|
|
||||||
' <log>
|
|
||||||
<datetime>%s</datetime>
|
|
||||||
<component>%s</component>
|
|
||||||
<server>%s</server>
|
|
||||||
<pid>%d</pid>
|
|
||||||
<level>%s</level>
|
|
||||||
<message><![CDATA[%s]]></message>
|
|
||||||
<file>%s</file>
|
|
||||||
<line>%d</line>
|
|
||||||
</log>
|
|
||||||
', $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], utf8_decode( $log['Message'] ), $log['File'], $log['Line'] );
|
|
||||||
}
|
|
||||||
fwrite( $exportFP,
|
|
||||||
' </logs>
|
|
||||||
</logexport>' );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$exportExt = 'xml';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
fclose( $exportFP );
|
|
||||||
ajaxResponse( array(
|
|
||||||
'key' => $exportKey,
|
|
||||||
'format' => $format,
|
|
||||||
) );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'download' :
|
|
||||||
{
|
|
||||||
if ( !canView('System') )
|
|
||||||
ajaxError('Insufficient permissions to download logs');
|
|
||||||
|
|
||||||
if ( empty($_REQUEST['key']) )
|
|
||||||
ZM\Fatal('No log export key given');
|
|
||||||
$exportKey = $_REQUEST['key'];
|
|
||||||
if ( empty($_REQUEST['format']) )
|
|
||||||
ZM\Fatal('No log export format given');
|
|
||||||
$format = $_REQUEST['format'];
|
|
||||||
|
|
||||||
switch ( $format ) {
|
|
||||||
case 'text' :
|
|
||||||
$exportExt = 'txt';
|
|
||||||
break;
|
|
||||||
case 'tsv' :
|
|
||||||
$exportExt = 'tsv';
|
|
||||||
break;
|
|
||||||
case 'html' :
|
|
||||||
$exportExt = 'html';
|
|
||||||
break;
|
|
||||||
case 'xml' :
|
|
||||||
$exportExt = 'xml';
|
|
||||||
break;
|
|
||||||
default :
|
|
||||||
ZM\Fatal("Unrecognised log export format '$format'");
|
|
||||||
}
|
|
||||||
|
|
||||||
$exportFile = 'zm-log.'.$exportExt;
|
|
||||||
$exportPath = ZM_DIR_EXPORTS.'/zm-log-'.$exportKey.$exportExt;
|
|
||||||
|
|
||||||
header('Pragma: public');
|
|
||||||
header('Expires: 0');
|
|
||||||
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
|
||||||
header('Cache-Control: private', false); // required by certain browsers
|
|
||||||
header('Content-Description: File Transfer');
|
|
||||||
header('Content-Disposition: attachment; filename="'.$exportFile.'"');
|
|
||||||
header('Content-Transfer-Encoding: binary');
|
|
||||||
header('Content-Type: application/force-download');
|
|
||||||
header('Content-Length: '.filesize($exportPath));
|
|
||||||
readfile($exportPath);
|
|
||||||
exit(0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} // end switch ( $_REQUEST['task'] )
|
|
||||||
ajaxError('Unrecognised action or insufficient permissions');
|
|
||||||
?>
|
|
||||||
|
|
|
@ -1,132 +0,0 @@
|
||||||
<?php
|
|
||||||
global $Servers;
|
|
||||||
|
|
||||||
if ( !canView('System') ) {
|
|
||||||
ajaxError('Insufficient permissions to view log entries');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only the query task is supported at the moment
|
|
||||||
if ( !isset($_REQUEST['task']) or $_REQUEST['task'] != 'query' ) {
|
|
||||||
ajaxError('Unrecognised action');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// The table we want our data from
|
|
||||||
$table = 'Logs';
|
|
||||||
|
|
||||||
// The names of the dB columns in the log table we are interested in
|
|
||||||
$columns = array('TimeKey', 'Component', 'ServerId', 'Pid', 'Code', 'Message', 'File', 'Line');
|
|
||||||
|
|
||||||
// The names of columns shown in the log view that are NOT dB columns in the database
|
|
||||||
$col_alt = array('DateTime', 'Server');
|
|
||||||
|
|
||||||
// Search contains a user entered string to search on
|
|
||||||
$search = isset($_REQUEST['search']) ? $_REQUEST['search'] : '';
|
|
||||||
|
|
||||||
// Advanced search contains an array of "column name" => "search text" pairs
|
|
||||||
// Bootstrap table sends json_ecoded array, which we must decode
|
|
||||||
$advsearch = isset($_REQUEST['filter']) ? json_decode($_REQUEST['filter'], JSON_OBJECT_AS_ARRAY) : array();
|
|
||||||
|
|
||||||
// Sort specifies the name of the column to sort on
|
|
||||||
$sort = 'TimeKey';
|
|
||||||
if ( isset($_REQUEST['sort']) ) {
|
|
||||||
if ( !in_array($_REQUEST['sort'], array_merge($columns, $col_alt)) ) {
|
|
||||||
ZM\Error('Invalid sort field: ' . $_REQUEST['sort']);
|
|
||||||
} else {
|
|
||||||
$sort = $_REQUEST['sort'];
|
|
||||||
if ( $sort == 'DateTime' ) $sort = 'TimeKey';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Offset specifies the starting row to return, used for pagination
|
|
||||||
$offset = 0;
|
|
||||||
if ( isset($_REQUEST['offset']) ) {
|
|
||||||
if ( ( !is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']) ) ) {
|
|
||||||
ZM\Error('Invalid value for offset: ' . $_REQUEST['offset']);
|
|
||||||
} else {
|
|
||||||
$offset = $_REQUEST['offset'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Order specifies the sort direction, either asc or desc
|
|
||||||
$order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc')) ? 'ASC' : 'DESC';
|
|
||||||
|
|
||||||
// Limit specifies the number of rows to return
|
|
||||||
$limit = 100;
|
|
||||||
if ( isset($_REQUEST['limit']) ) {
|
|
||||||
if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) {
|
|
||||||
ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']);
|
|
||||||
} else {
|
|
||||||
$limit = $_REQUEST['limit'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$col_str = implode(', ', $columns);
|
|
||||||
$data = array();
|
|
||||||
$query = array();
|
|
||||||
$query['values'] = array();
|
|
||||||
$likes = array();
|
|
||||||
$where = '';
|
|
||||||
// There are two search bars in the log view, normal and advanced
|
|
||||||
// Making an exuctive decision to ignore the normal search, when advanced search is in use
|
|
||||||
// Alternatively we could try to do both
|
|
||||||
if ( count($advsearch) ) {
|
|
||||||
|
|
||||||
foreach ( $advsearch as $col=>$text ) {
|
|
||||||
if ( !in_array($col, array_merge($columns, $col_alt)) ) {
|
|
||||||
ZM\Error("'$col' is not a sortable column name");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$text = '%' .$text. '%';
|
|
||||||
array_push($likes, $col.' LIKE ?');
|
|
||||||
array_push($query['values'], $text);
|
|
||||||
}
|
|
||||||
$wherevalues = $query['values'];
|
|
||||||
$where = ' WHERE (' .implode(' OR ', $likes). ')';
|
|
||||||
|
|
||||||
} else if ( $search != '' ) {
|
|
||||||
|
|
||||||
$search = '%' .$search. '%';
|
|
||||||
foreach ( $columns as $col ) {
|
|
||||||
array_push($likes, $col.' LIKE ?');
|
|
||||||
array_push($query['values'], $search);
|
|
||||||
}
|
|
||||||
$wherevalues = $query['values'];
|
|
||||||
$where = ' WHERE (' .implode(' OR ', $likes). ')';
|
|
||||||
}
|
|
||||||
|
|
||||||
$query['sql'] = 'SELECT ' .$col_str. ' FROM `' .$table. '` ' .$where. ' ORDER BY ' .$sort. ' ' .$order. ' LIMIT ?, ?';
|
|
||||||
array_push($query['values'], $offset, $limit);
|
|
||||||
|
|
||||||
//ZM\Warning('Calling the following sql query: ' .$query['sql']);
|
|
||||||
|
|
||||||
$data['totalNotFiltered'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table, 'Total');
|
|
||||||
if ( $search != '' || count($advsearch) ) {
|
|
||||||
$data['total'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table.$where , 'Total', $wherevalues);
|
|
||||||
} else {
|
|
||||||
$data['total'] = $data['totalNotFiltered'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !$Servers )
|
|
||||||
$Servers = ZM\Server::find();
|
|
||||||
$servers_by_Id = array();
|
|
||||||
# There is probably a better way to do this.
|
|
||||||
foreach ( $Servers as $server ) {
|
|
||||||
$servers_by_Id[$server->Id()] = $server;
|
|
||||||
}
|
|
||||||
|
|
||||||
$rows = array();
|
|
||||||
foreach ( dbFetchAll($query['sql'], NULL, $query['values']) as $row ) {
|
|
||||||
$row['DateTime'] = strftime('%Y-%m-%d %H:%M:%S', intval($row['TimeKey']));
|
|
||||||
$row['Server'] = ( $row['ServerId'] and isset($servers_by_Id[$row['ServerId']]) ) ? $servers_by_Id[$row['ServerId']]->Name() : '';
|
|
||||||
// First strip out any html tags
|
|
||||||
// Second strip out all characters that are not ASCII 32-126 (yes, 126)
|
|
||||||
$row['Message'] = preg_replace('/[^\x20-\x7E]/', '', strip_tags($row['Message']));
|
|
||||||
$rows[] = $row;
|
|
||||||
}
|
|
||||||
$data['rows'] = $rows;
|
|
||||||
$data['logstate'] = logState();
|
|
||||||
$data['updated'] = preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG);
|
|
||||||
|
|
||||||
ajaxResponse($data);
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* bootstrap-table - An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation)
|
* bootstrap-table - An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation)
|
||||||
*
|
*
|
||||||
* @version v1.18.0
|
* @version v1.17.1
|
||||||
* @homepage https://bootstrap-table.com
|
* @homepage https://bootstrap-table.com
|
||||||
* @author wenzhixin <wenzhixin2010@gmail.com> (http://wenzhixin.net.cn/)
|
* @author wenzhixin <wenzhixin2010@gmail.com> (http://wenzhixin.net.cn/)
|
||||||
* @license MIT
|
* @license MIT
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* bootstrap-table - An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation)
|
* bootstrap-table - An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation)
|
||||||
*
|
*
|
||||||
* @version v1.18.0
|
* @version v1.17.1
|
||||||
* @homepage https://bootstrap-table.com
|
* @homepage https://bootstrap-table.com
|
||||||
* @author wenzhixin <wenzhixin2010@gmail.com> (http://wenzhixin.net.cn/)
|
* @author wenzhixin <wenzhixin2010@gmail.com> (http://wenzhixin.net.cn/)
|
||||||
* @license MIT
|
* @license MIT
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -215,7 +215,7 @@ if ( ($codec == 'MP4' || $codec == 'auto' ) && $Event->DefaultVideo() ) {
|
||||||
array_map(function($r){return $r/100;},
|
array_map(function($r){return $r/100;},
|
||||||
array_filter(
|
array_filter(
|
||||||
array_keys($rates),
|
array_keys($rates),
|
||||||
function($r){return $r >= 0 ? true : false;},
|
function($r){return $r >= 0 ? true : false;}
|
||||||
))) ?>], "plugins": { "zoomrotate": { "zoom": "<?php echo $Zoom ?>"}}}'
|
))) ?>], "plugins": { "zoomrotate": { "zoom": "<?php echo $Zoom ?>"}}}'
|
||||||
>
|
>
|
||||||
<source src="<?php echo $Event->getStreamSrc(array('mode'=>'mpeg','format'=>'h264'),'&'); ?>" type="video/mp4">
|
<source src="<?php echo $Event->getStreamSrc(array('mode'=>'mpeg','format'=>'h264'),'&'); ?>" type="video/mp4">
|
||||||
|
|
|
@ -1,3 +1,77 @@
|
||||||
|
var backBtn = $j('#backBtn');
|
||||||
|
var viewBtn = $j('#viewBtn');
|
||||||
|
var archiveBtn = $j('#archiveBtn');
|
||||||
|
var unarchiveBtn = $j('#unarchiveBtn');
|
||||||
|
var editBtn = $j('#editBtn');
|
||||||
|
var exportBtn = $j('#exportBtn');
|
||||||
|
var downloadBtn = $j('#downloadBtn');
|
||||||
|
var deleteBtn = $j('#deleteBtn');
|
||||||
|
var table = $j('#eventTable');
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is the format of the json object sent by bootstrap-table
|
||||||
|
|
||||||
|
var params =
|
||||||
|
{
|
||||||
|
"type":"get",
|
||||||
|
"data":
|
||||||
|
{
|
||||||
|
"search":"some search text",
|
||||||
|
"sort":"StartTime",
|
||||||
|
"order":"asc",
|
||||||
|
"offset":0,
|
||||||
|
"limit":25
|
||||||
|
"filter":
|
||||||
|
{
|
||||||
|
"Name":"some advanced search text"
|
||||||
|
"StartTime":"some more advanced search text"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cache":true,
|
||||||
|
"contentType":"application/json",
|
||||||
|
"dataType":"json"
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Called by bootstrap-table to retrieve zm event data
|
||||||
|
function ajaxRequest(params) {
|
||||||
|
$j.getJSON(thisUrl + '?view=request&request=events&task=query', params.data)
|
||||||
|
.done(function(data) {
|
||||||
|
//console.log('Ajax parameters: ' + JSON.stringify(params));
|
||||||
|
// rearrange the result into what bootstrap-table expects
|
||||||
|
var rows = processRows(data.rows);
|
||||||
|
params.success({total: data.total, totalNotFiltered: data.totalNotFiltered, rows: rows});
|
||||||
|
})
|
||||||
|
.fail(logAjaxFail);
|
||||||
|
}
|
||||||
|
|
||||||
|
function processRows(rows) {
|
||||||
|
// WIP: Inject desired html and formatting for the cells in each row
|
||||||
|
// REMINDER: Make these lines dependent on user permissions e.g. canEditEvents
|
||||||
|
$j.each(rows, function(ndx, row) {
|
||||||
|
var eid = row.Id;
|
||||||
|
var mid = row.MonitorId;
|
||||||
|
var archived = row.Archived == yesString ? archivedString : '';
|
||||||
|
var emailed = row.Emailed == yesString ? emailedString : '';
|
||||||
|
|
||||||
|
row.Id = '<a href="?view=event&eid=' + eid + filterQuery + sortQuery + '&page=1">' + eid + '</a>';
|
||||||
|
row.Name = '<a href="?view=event&eid=' + eid + filterQuery + sortQuery + '&page=1">' + row.Name + '</a>'
|
||||||
|
+ '<br/><div class="small text-nowrap text-muted">' + archived + emailed + '</div>';
|
||||||
|
row.Monitor = '<a href="?view=monitor&mid=' + mid + '">' + row.Monitor + '</a>';
|
||||||
|
row.Cause = '<a href="#" title="' + row.Notes + '" class="eDetailLink" data-eid="' + eid + '">' + row.Cause + '</a>';
|
||||||
|
if ( row.Notes.indexOf('detected:') >= 0 ) {
|
||||||
|
row.Cause = row.Cause + '<a href="#?view=image&eid=' + eid + '&fid=objdetect"><div class="small text-nowrap text-muted"><u>' + row.Notes + '</u></div></a>';
|
||||||
|
} else if ( row.Notes != 'Forced Web: ' ) {
|
||||||
|
row.Cause = row.Cause + '<br/><div class="small text-nowrap text-muted">' + row.Notes + '</div>';
|
||||||
|
}
|
||||||
|
row.Frames = '<a href="?view=frames&eid=' + eid + '">' + row.Frames + '</a>';
|
||||||
|
row.AlarmFrames = '<a href="?view=frames&eid=' + eid + '">' + row.AlarmFrames + '</a>';
|
||||||
|
row.MaxScore = '<a href="?view=frame&eid=' + eid + '&fid=0">' + row.MaxScore + '</a>';
|
||||||
|
});
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
function thumbnail_onmouseover(event) {
|
function thumbnail_onmouseover(event) {
|
||||||
var img = event.target;
|
var img = event.target;
|
||||||
img.src = '';
|
img.src = '';
|
||||||
|
@ -56,7 +130,7 @@ function manageDelConfirmModalBtns() {
|
||||||
var selections = getIdSelections();
|
var selections = getIdSelections();
|
||||||
|
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
$j.getJSON(thisUrl + '?request=events&action=delete&eids[]='+selections.join('&eids[]='))
|
$j.getJSON(thisUrl + '?request=events&task=delete&eids[]='+selections.join('&eids[]='))
|
||||||
.done( function(data) {
|
.done( function(data) {
|
||||||
$j('#eventTable').bootstrapTable('refresh');
|
$j('#eventTable').bootstrapTable('refresh');
|
||||||
window.location.reload(true);
|
window.location.reload(true);
|
||||||
|
@ -85,16 +159,6 @@ function getEventDetailModal(eid) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function initPage() {
|
function initPage() {
|
||||||
var backBtn = $j('#backBtn');
|
|
||||||
var viewBtn = $j('#viewBtn');
|
|
||||||
var archiveBtn = $j('#archiveBtn');
|
|
||||||
var unarchiveBtn = $j('#unarchiveBtn');
|
|
||||||
var editBtn = $j('#editBtn');
|
|
||||||
var exportBtn = $j('#exportBtn');
|
|
||||||
var downloadBtn = $j('#downloadBtn');
|
|
||||||
var deleteBtn = $j('#deleteBtn');
|
|
||||||
var table = $j('#eventTable');
|
|
||||||
|
|
||||||
// Load the delete confirmation modal into the DOM
|
// Load the delete confirmation modal into the DOM
|
||||||
getDelConfirmModal();
|
getDelConfirmModal();
|
||||||
|
|
||||||
|
@ -169,7 +233,7 @@ function initPage() {
|
||||||
var selections = getIdSelections();
|
var selections = getIdSelections();
|
||||||
|
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
$j.getJSON(thisUrl + '?request=events&action=archive&eids[]='+selections.join('&eids[]='))
|
$j.getJSON(thisUrl + '?request=events&task=archive&eids[]='+selections.join('&eids[]='))
|
||||||
.done( function(data) {
|
.done( function(data) {
|
||||||
$j('#eventTable').bootstrapTable('refresh');
|
$j('#eventTable').bootstrapTable('refresh');
|
||||||
window.location.reload(true);
|
window.location.reload(true);
|
||||||
|
@ -188,7 +252,7 @@ function initPage() {
|
||||||
console.log(selections);
|
console.log(selections);
|
||||||
|
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
$j.getJSON(thisUrl + '?request=events&action=unarchive&eids[]='+selections.join('&eids[]='))
|
$j.getJSON(thisUrl + '?request=events&task=unarchive&eids[]='+selections.join('&eids[]='))
|
||||||
.done( function(data) {
|
.done( function(data) {
|
||||||
$j('#eventTable').bootstrapTable('refresh');
|
$j('#eventTable').bootstrapTable('refresh');
|
||||||
window.location.reload(true);
|
window.location.reload(true);
|
||||||
|
@ -262,6 +326,16 @@ function initPage() {
|
||||||
getEventDetailModal(eid);
|
getEventDetailModal(eid);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update table links each time after new data is loaded
|
||||||
|
table.on('post-body.bs.table', function(data) {
|
||||||
|
// Manage the eventdetail links in the events list
|
||||||
|
$j(".eDetailLink").click(function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
var eid = $j(this).data('eid');
|
||||||
|
getEventDetailModal(eid);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// The table is initially given a hidden style, so now that we are done rendering, show it
|
// The table is initially given a hidden style, so now that we are done rendering, show it
|
||||||
table.show();
|
table.show();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,3 +7,7 @@ var filterQuery = '<?php echo isset($filterQuery)?validJsStr(htmlspecialchars_de
|
||||||
var sortQuery = '<?php echo isset($sortQuery)?validJsStr(htmlspecialchars_decode($sortQuery)):'' ?>';
|
var sortQuery = '<?php echo isset($sortQuery)?validJsStr(htmlspecialchars_decode($sortQuery)):'' ?>';
|
||||||
|
|
||||||
var confirmDeleteEventsString = "<?php echo addslashes(translate('ConfirmDeleteEvents')) ?>";
|
var confirmDeleteEventsString = "<?php echo addslashes(translate('ConfirmDeleteEvents')) ?>";
|
||||||
|
var archivedString = "<?php echo translate('Archived') ?>";
|
||||||
|
var emailedString = "<?php echo translate('Emailed') ?>";
|
||||||
|
var yesString = "<?php echo translate('Yes') ?>";
|
||||||
|
var noString = "<?php echo translate('No') ?>";
|
||||||
|
|
|
@ -27,7 +27,7 @@ var params =
|
||||||
|
|
||||||
// Called by bootstrap-table to retrieve zm log data
|
// Called by bootstrap-table to retrieve zm log data
|
||||||
function ajaxRequest(params) {
|
function ajaxRequest(params) {
|
||||||
$j.getJSON(thisUrl + '?view=request&request=newlog&task=query', params.data)
|
$j.getJSON(thisUrl + '?view=request&request=log&task=query', params.data)
|
||||||
.done(function(data) {
|
.done(function(data) {
|
||||||
//console.log('Ajax parameters: ' + JSON.stringify(params));
|
//console.log('Ajax parameters: ' + JSON.stringify(params));
|
||||||
// rearrange the result into what bootstrap-table expects
|
// rearrange the result into what bootstrap-table expects
|
||||||
|
|
|
@ -182,7 +182,9 @@ function applyPreset() {
|
||||||
function toPixels(field, maxValue) {
|
function toPixels(field, maxValue) {
|
||||||
if ( field.value != '' ) {
|
if ( field.value != '' ) {
|
||||||
field.value = Math.round((field.value*maxValue)/100);
|
field.value = Math.round((field.value*maxValue)/100);
|
||||||
if ( field.value > maxValue ) field.value = maxValue;
|
if ( field.value > maxValue ) {
|
||||||
|
field.value = maxValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
field.setAttribute('step', 1);
|
field.setAttribute('step', 1);
|
||||||
field.setAttribute('max', maxValue);
|
field.setAttribute('max', maxValue);
|
||||||
|
|
Loading…
Reference in New Issue