Merge branch 'master' of github.com:ZoneMinder/zoneminder

This commit is contained in:
Isaac Connor 2020-10-20 16:04:23 -04:00
commit 5fa70da092
29 changed files with 516 additions and 687 deletions

View File

@ -1,41 +1,214 @@
<?php
ini_set('display_errors', '0');
if ( empty($_REQUEST['eids']) ) {
ajaxError('No event id(s) supplied');
$message = '';
$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') ) {
} // end if canView('Events')
if ( empty($_REQUEST['eids']) ) {
if ( isset($_REQUEST['task']) && $_REQUEST['task'] != "query" ) $message = 'No event id(s) supplied';
} else {
$eids = $_REQUEST['eids'];
}
if ( canEdit('Events') ) {
if ( $message ) {
ajaxError($message);
return;
}
// 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 = '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 'unarchive' :
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(
'UPDATE Events SET Archived = ? WHERE Id = ?',
array($archiveVal, $eid)
);
}
function deleteRequest($eid) {
$message = array();
$event = new ZM\Event($eid);
if ( !$event->Id() ) {
$message[] = array($eid=>'Event not found.');
} else if ( $event->Archived() ) {
$message[] = array($eid=>'Event is archived, cannot delete it.');
} else {
$event->delete();
}
return $message;
}
foreach ( $_REQUEST['eids'] as $eid ) {
function queryRequest($search, $advsearch, $sort, $offset, $order, $limit) {
// Put server pagination code here
// The table we want our data from
$table = 'Events';
switch ( $_REQUEST['action'] ) {
case 'archive' :
case 'unarchive' :
$archiveVal = ($_REQUEST['action'] == 'archive') ? 1 : 0;
dbQuery(
'UPDATE Events SET Archived = ? WHERE Id = ?',
array($archiveVal, $eid)
);
break;
case 'delete' :
$event = new ZM\Event($eid);
if ( !$event->Id() ) {
$message[] = array($eid=>'Event not found.');
} else if ( $event->Archived() ) {
$message[] = array($eid=>'Event is archived, cannot delete it.');
} else {
$event->delete();
// 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;
}
break;
} // end switch action
} // end foreach
ajaxResponse($message);
} // end if canEdit('Events')
$text = '%' .$text. '%';
array_push($likes, $col.' LIKE ?');
array_push($query['values'], $text);
}
$wherevalues = $query['values'];
$where = ' WHERE (' .implode(' OR ', $likes). ')';
ajaxError('Unrecognised action '.$_REQUEST['action'].' or insufficient permissions for user '.$user['Username']);
} 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 ) {
$event = new ZM\Event($row['Id']);
$scale = intval(5*100*ZM_WEB_LIST_THUMB_WIDTH / $event->Width());
$imgSrc = $event->getThumbnailSrc(array(),'&amp;');
$streamSrc = $event->getStreamSrc(array(
'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single', 'rate'=>'400'), '&amp;');
// Modify the row data as needed
$row['imgHtml'] = '<img id="thumbnail' .$event->Id(). '" src="' .$imgSrc. '" alt="' .validHtmlStr('Event ' .$event->Id()). '" style="width:' .validInt($event->ThumbnailWidth()). 'px;height:' .validInt($event->ThumbnailHeight()).'px;" stream_src="' .$streamSrc. '" still_src="' .$imgSrc. '"/>';
$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;
}
?>

View File

@ -1,466 +1,132 @@
<?php
ini_set('display_errors', '0');
global $Servers;
# Moved up here because it is used in several spots.
# These are the valid columns that you can filter on.
$filterFields = array('Component', 'ServerId', 'Pid', 'Level', 'File', 'Line');
if ( !canView('System') ) {
ajaxError('Insufficient permissions to view log entries');
return;
}
function buildLogQuery($action) {
global $filterFields;
// 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';
$minTime = isset($_REQUEST['minTime']) ? $_REQUEST['minTime'] : NULL;
$maxTime = isset($_REQUEST['maxTime']) ? $_REQUEST['maxTime'] : NULL;
// The names of the dB columns in the log table we are interested in
$columns = array('TimeKey', 'Component', 'ServerId', 'Pid', 'Code', 'Message', 'File', 'Line');
$limit = 100;
if ( isset($_REQUEST['limit']) ) {
if ( ( !is_integer($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) {
ZM\Error('Invalid value for limit ' . $_REQUEST['limit']);
} else {
$limit = $_REQUEST['limit'];
}
// 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';
}
$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';
$where = array();
$values = array();
if ( $minTime ) {
$where[] = 'TimeKey > ?';
$values[] = $minTime;
} elseif ( $maxTime ) {
$where[] = 'TimeKey < ?';
$values[] = $maxTime;
// 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'];
}
}
foreach ( $filter as $field=>$value ) {
if ( !in_array($field, $filterFields) ) {
ZM\Error("'$field' is not in valid filter fields " . print_r($filterField, true));
// 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;
}
if ( $field == 'Level' ) {
$where[] = $field.' <= ?';
$values[] = $value;
} else {
$where[] = $field.' = ?';
$values[] = $value;
}
$text = '%' .$text. '%';
array_push($likes, $col.' LIKE ?');
array_push($query['values'], $text);
}
if ( count($where) )
$sql.= ' WHERE '.join(' AND ', $where);
$sql .= ' ORDER BY '.$sortField.' '.$sortOrder.' LIMIT '.$limit;
$wherevalues = $query['values'];
$where = ' WHERE (' .implode(' OR ', $likes). ')';
return array('sql'=>$sql, 'values'=>$values);
} # function buildLogQuery($action)
} else if ( $search != '' ) {
switch ( $_REQUEST['task'] ) {
case 'create' :
{
// Silently ignore bogus requests
if ( !empty($_POST['level']) && !empty($_POST['message']) ) {
ZM\logInit(array('id'=>'web_js'));
$string = $_POST['message'];
$file = !empty($_POST['file']) ? preg_replace('/\w+:\/\/[\w.:]+\//', '', $_POST['file']) : '';
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;
$search = '%' .$search. '%';
foreach ( $columns as $col ) {
array_push($likes, $col.' LIKE ?');
array_push($query['values'], $search);
}
case 'delete' :
{
if ( !canEdit('System') )
ajaxError('Insufficient permissions to delete log entries');
$wherevalues = $query['values'];
$where = ' WHERE (' .implode(' OR ', $likes). ')';
}
$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 *');
$query['sql'] = 'SELECT ' .$col_str. ' FROM `' .$table. '` ' .$where. ' ORDER BY ' .$sort. ' ' .$order. ' LIMIT ?, ?';
array_push($query['values'], $offset, $limit);
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;
}
//ZM\Warning('Calling the following sql query: ' .$query['sql']);
$logs = array();
$options = array();
$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'];
}
foreach ( dbFetchAll($query['sql'], NULL, $query['values']) as $log ) {
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;
}
$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];
$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);
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
ajaxResponse($data);
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');
?>

View File

@ -56,6 +56,7 @@ while ( $row = $result->fetch(PDO::FETCH_ASSOC) ) {
ZM\Warning('Failed to decode ' . $row['data']);
continue;
}
ZM\Debug(print_r($_SESSION, true));
if ( isset($_SESSION['last_time']) ) {
# This is a dead session
continue;
@ -91,12 +92,11 @@ $_SESSION = $current_session;
<?php } # end if canView(System) ?>
</div>
<div class="modal-footer">
<form name="logoutForm" id="logoutForm" method="post" action="?">
<form name="logoutForm" id="logoutForm" method="post" action="?view=logout">
<?php
// We have to manually insert the csrf key into the form when using a modal generated via ajax call
echo getCSRFinputHTML();
?>
<input type="hidden" name="view" value="logout"/>
<button type="submit" name="action" value="logout"><?php echo translate('Logout') ?></button>
<?php if ( ZM_USER_SELF_EDIT ) echo '<button type="submit" name="action" value="config">'.translate('Config').'</button>'.PHP_EOL; ?>
<button type="button" data-dismiss="modal"><?php echo translate('Cancel') ?></button>

View File

@ -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);

View File

@ -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)
*
* @version v1.18.0
* @version v1.17.1
* @homepage https://bootstrap-table.com
* @author wenzhixin <wenzhixin2010@gmail.com> (http://wenzhixin.net.cn/)
* @license MIT

View File

@ -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)
*
* @version v1.18.0
* @version v1.17.1
* @homepage https://bootstrap-table.com
* @author wenzhixin <wenzhixin2010@gmail.com> (http://wenzhixin.net.cn/)
* @license MIT

View File

@ -2408,4 +2408,11 @@ function zm_random_bytes($length = 32) {
}
ZM\Error('No random_bytes function found.');
}
function i18n() {
$string = explode('_', ZM_LANG_DEFAULT, 2);
$string[1] = strtoupper($string[1]);
return implode('-', $string);
}
?>

View File

@ -65,9 +65,9 @@
//
// Examples
// setlocale( 'LC_ALL', 'en_GB' ); All locale settings pre-4.3.0
setlocale( LC_ALL, 'cn_ZH' ); //All locale settings 4.3.0 and after
setlocale( LC_CTYPE, 'cn_ZH' ); //Character class settings 4.3.0 and after
setlocale( LC_TIME, 'cn_ZH' ); //Date and time formatting 4.3.0 and after
setlocale( LC_ALL, 'zh_CN' ); //All locale settings 4.3.0 and after
setlocale( LC_CTYPE, 'zh_CN' ); //Character class settings 4.3.0 and after
setlocale( LC_TIME, 'zh_CN' ); //Date and time formatting 4.3.0 and after
// Simple String Replacements
$SLANG = array(

View File

@ -790,6 +790,7 @@ function getStatsTableHTML($eid, $fid, $row='') {
$stats = dbFetchAll( $sql, NULL, array( $eid, $fid ) );
$result .= '<table id="contentStatsTable' .$row. '"'.PHP_EOL;
$result .= 'data-locale="' .i18n(). '"'.PHP_EOL;
$result .= 'data-toggle="table"'.PHP_EOL;
$result .= 'data-toolbar="#toolbar"'.PHP_EOL;
$result .= 'class="table-sm table-borderless contentStatsTable"'.PHP_EOL;
@ -875,6 +876,7 @@ function xhtmlFooter() {
<script src="skins/<?php echo $skin; ?>/js/bootstrap.min.js"></script>
<?php echo output_script_if_exists(array(
'js/bootstrap-table.min.js',
'js/bootstrap-table-locale-all.min.js',
'js/tableExport.min.js',
'js/bootstrap-table-export.min.js',
'js/bootstrap-table-page-jump-to.min.js',

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -240,7 +240,7 @@ if ( currentView != 'none' && currentView != 'login' ) {
$j(document).ready(function() {
// Load the Logout and State modals into the dom
$j('#logoutButton').click(getLogoutModal());
$j('#logoutButton').click(clickLogout);
if ( canEditSystem ) $j('#stateModalBtn').click(getStateModal);
// Trigger autorefresh of the widget bar stats on the navbar
@ -665,9 +665,18 @@ function getLogoutModal() {
$j.getJSON(thisUrl + '?request=modal&modal=logout')
.done(function(data) {
insertModalHtml('modalLogout', data.html);
manageModalBtns('modalLogout');
clickLogout();
})
.fail(logAjaxFail);
}
function clickLogout() {
if ( ! $j('#modalLogout').length ) {
getLogoutModal();
return;
}
$j('#modalLogout').modal('show');
}
function getStateModal() {
$j.getJSON(thisUrl + '?request=modal&modal=state')
@ -769,13 +778,14 @@ function getModal(id) {
}
function manageModalBtns(id) {
// Manage the CANCEL modal button
// Manage the CANCEL modal button, note data-dismiss="modal" would work better
var cancelBtn = document.getElementById(id+"CancelBtn");
if ( cancelBtn ) {
document.getElementById(id+"CancelBtn").addEventListener('click', function onCancelClick(evt) {
$j('#'+id).modal('hide');
});
}
// 'data-on-click-this' calls the global function in the attribute value with the element when a click happens.
document.querySelectorAll('#'+id+'Modal button[data-on-click]').forEach(function attachOnClick(el) {
var fnName = el.getAttribute('data-on-click');

View File

@ -44,6 +44,8 @@ var canEditSystem = <?php echo canEdit('System')?'true':'false' ?>;
var canViewSystem = <?php echo canView('System')?'true':'false' ?>;
var canEditEvents = <?php echo canEdit('Events')?'true':'false' ?>;
var canViewEvents = <?php echo canView('Events')?'true':'false' ?>;
var canEditMonitors = <?php echo canEdit('Monitors')?'true':'false' ?>;
var canViewMonitors = <?php echo canView('Monitors')?'true':'false' ?>;
var canEditGroups = <?php echo canEdit('Groups')?'true':'false' ?>;

View File

@ -45,6 +45,7 @@ xhtmlHeaders(__FILE__, translate('ControlCaps'));
<div id="content">
<table
id="controlTable"
data-locale="<?php echo i18n() ?>"
class="table-sm table-borderless"
data-search="true"
data-cookie="true"

View File

@ -49,6 +49,7 @@ xhtmlHeaders(__FILE__, translate('Devices') );
<div id="content" class="row justify-content-center">
<table
id="devicesTable"
data-locale="<?php echo i18n() ?>"
class="table-sm table-borderless"
data-search="true"
data-cookie="true"

View File

@ -215,7 +215,7 @@ if ( ($codec == 'MP4' || $codec == 'auto' ) && $Event->DefaultVideo() ) {
array_map(function($r){return $r/100;},
array_filter(
array_keys($rates),
function($r){return $r >= 0 ? true : false;},
function($r){return $r >= 0 ? true : false;}
))) ?>], "plugins": { "zoomrotate": { "zoom": "<?php echo $Zoom ?>"}}}'
>
<source src="<?php echo $Event->getStreamSrc(array('mode'=>'mpeg','format'=>'h264'),'&amp;'); ?>" type="video/mp4">

View File

@ -135,6 +135,7 @@ getBodyTopHTML();
<div class="row justify-content-center">
<table
id="eventTable"
data-locale="<?php echo i18n() ?>"
data-pagination="true"
data-show-pagination-switch="true"
data-page-list="[10, 25, 50, 100, 200, All]"

View File

@ -125,6 +125,7 @@ xhtmlHeaders(__FILE__, translate('Frames').' - '.$Event->Id());
<div class="row justify-content-center">
<table
id="framesTable"
data-locale="<?php echo i18n() ?>"
data-pagination="true"
data-show-pagination-switch="true"
data-page-list="[10, 25, 50, 100, 200, All]"

View File

@ -128,45 +128,55 @@ function reloadWindow() {
}
// Manage the the Function modal and its buttons
function manageFunctionModal() {
$j('.functionLnk').click(function(evt) {
evt.preventDefault();
if ( !canEditEvents ) {
enoperm();
return;
}
var mid = evt.currentTarget.getAttribute('data-mid');
monitor = monitors[mid];
if ( !monitor ) {
console.error("No monitor found for mid " + mid);
return;
}
function manageFunctionModal(evt) {
evt.preventDefault();
var function_form = document.getElementById('function_form');
if ( !function_form ) {
console.error("Unable to find form with id function_form");
return;
}
function_form.elements['newFunction'].value = monitor.Function;
function_form.elements['newEnabled'].checked = monitor.Enabled == '1';
function_form.elements['mid'].value = mid;
document.getElementById('function_monitor_name').innerHTML = monitor.Name;
if ( !canEditEvents ) {
enoperm();
return;
}
$j('#modalFunction').modal('show');
});
if ( ! $j('#modalFunction').length ) {
// Load the Function modal on page load
$j.getJSON(thisUrl + '?request=modal&modal=function')
.done(function(data) {
insertModalHtml('modalFunction', data.html);
// Manage the CANCEL modal buttons
$j('.funcCancelBtn').click(function(evt) {
evt.preventDefault();
$j('#modalFunction').modal('hide');
});
// Manage the SAVE modal buttons
$j('.funcSaveBtn').click(function(evt) {
evt.preventDefault();
$j('#function_form').submit();
});
// Manage the CANCEL modal buttons
$j('.funcCancelBtn').click(function(evt) {
evt.preventDefault();
$j('#modalFunction').modal('hide');
});
manageFunctionModal(evt);
})
.fail(logAjaxFail);
return;
}
// Manage the SAVE modal buttons
$j('.funcSaveBtn').click(function(evt) {
evt.preventDefault();
$j('#function_form').submit();
});
}
var mid = evt.currentTarget.getAttribute('data-mid');
monitor = monitors[mid];
if ( !monitor ) {
console.error("No monitor found for mid " + mid);
return;
}
var function_form = document.getElementById('function_form');
if ( !function_form ) {
console.error("Unable to find form with id function_form");
return;
}
function_form.elements['newFunction'].value = monitor.Function;
function_form.elements['newEnabled'].checked = monitor.Enabled == '1';
function_form.elements['mid'].value = mid;
document.getElementById('function_monitor_name').innerHTML = monitor.Name;
$j('#modalFunction').modal('show');
} // end function manageFunctionModal
function initPage() {
reloadWindow.periodical(consoleRefreshTimeout);
@ -196,15 +206,8 @@ function initPage() {
// Setup the thumbnail video animation
initThumbAnimation();
// Load the Function modal on page load
$j.getJSON(thisUrl + '?request=modal&modal=function')
.done(function(data) {
insertModalHtml('modalFunction', data.html);
// Manage the Function modal
manageFunctionModal();
})
.fail(logAjaxFail);
}
$j('.functionLnk').click(manageFunctionModal);
} // end function initPage
function applySort(event, ui) {
var monitor_ids = $j(this).sortable('toArray');

View File

@ -1,3 +1,75 @@
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) {
var rows = processRows(data.rows);
// rearrange the result into what bootstrap-table expects
params.success({total: data.total, totalNotFiltered: data.totalNotFiltered, rows: rows});
})
.fail(logAjaxFail);
}
function processRows(rows) {
$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&amp;eid=' + eid + filterQuery + sortQuery + '&amp;page=1">' + eid + '</a>';
row.Name = '<a href="?view=event&amp;eid=' + eid + filterQuery + sortQuery + '&amp;page=1">' + row.Name + '</a>'
+ '<br/><div class="small text-nowrap text-muted">' + archived + emailed + '</div>';
if ( canEditMonitors ) row.Monitor = '<a href="?view=monitor&amp;mid=' + mid + '">' + row.Monitor + '</a>';
if ( canEditEvents ) 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&amp;eid=' + eid + '&amp;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&amp;eid=' + eid + '">' + row.Frames + '</a>';
row.AlarmFrames = '<a href="?view=frames&amp;eid=' + eid + '">' + row.AlarmFrames + '</a>';
row.MaxScore = '<a href="?view=frame&amp;eid=' + eid + '&amp;fid=0">' + row.MaxScore + '</a>';
row.Thumbnail = '<a href="?view=event&amp;eid=' + eid + filterQuery + sortQuery + '&amp;page=1">' + row.imgHtml + '</a>';
});
return rows;
}
function thumbnail_onmouseover(event) {
var img = event.target;
img.src = '';
@ -56,7 +128,7 @@ function manageDelConfirmModalBtns() {
var selections = getIdSelections();
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) {
$j('#eventTable').bootstrapTable('refresh');
window.location.reload(true);
@ -85,16 +157,6 @@ function getEventDetailModal(eid) {
}
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
getDelConfirmModal();
@ -169,7 +231,7 @@ function initPage() {
var selections = getIdSelections();
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) {
$j('#eventTable').bootstrapTable('refresh');
window.location.reload(true);
@ -188,7 +250,7 @@ function initPage() {
console.log(selections);
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) {
$j('#eventTable').bootstrapTable('refresh');
window.location.reload(true);
@ -262,6 +324,21 @@ function initPage() {
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);
});
var thumb_ndx = $j('#eventTable tr th').filter(function() {
return $j(this).text().trim() == 'Thumbnail';
}).index();
table.find("tr td:nth-child(" + (thumb_ndx+1) + ")").addClass('colThumbnail zoom');
});
// The table is initially given a hidden style, so now that we are done rendering, show it
table.show();
}

View File

@ -7,3 +7,7 @@ var filterQuery = '<?php echo isset($filterQuery)?validJsStr(htmlspecialchars_de
var sortQuery = '<?php echo isset($sortQuery)?validJsStr(htmlspecialchars_decode($sortQuery)):'' ?>';
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') ?>";

View File

@ -27,7 +27,7 @@ var params =
// Called by bootstrap-table to retrieve zm log data
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) {
//console.log('Ajax parameters: ' + JSON.stringify(params));
// rearrange the result into what bootstrap-table expects

View File

@ -182,7 +182,9 @@ function applyPreset() {
function toPixels(field, maxValue) {
if ( field.value != '' ) {
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('max', maxValue);

View File

@ -44,6 +44,7 @@ xhtmlHeaders(__FILE__, translate('SystemLog'));
<table
id="logTable"
data-locale="<?php echo i18n() ?>"
class="table-sm table-borderless"
data-side-pagination="server"
data-ajax="ajaxRequest"