Merge branch 'master' of github.com:ZoneMinder/zoneminder
This commit is contained in:
commit
5fa70da092
|
@ -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(),'&');
|
||||
$streamSrc = $event->getStreamSrc(array(
|
||||
'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single', 'rate'=>'400'), '&');
|
||||
|
||||
// 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;
|
||||
}
|
||||
?>
|
||||
|
|
554
web/ajax/log.php
554
web/ajax/log.php
|
@ -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');
|
||||
?>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
*
|
||||
* @version v1.18.0
|
||||
* @version v1.17.1
|
||||
* @homepage https://bootstrap-table.com
|
||||
* @author wenzhixin <wenzhixin2010@gmail.com> (http://wenzhixin.net.cn/)
|
||||
* @license MIT
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -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(
|
|
@ -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
|
@ -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');
|
||||
|
|
|
@ -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' ?>;
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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'),'&'); ?>" type="video/mp4">
|
||||
|
|
|
@ -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]"
|
||||
|
|
|
@ -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]"
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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&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>';
|
||||
if ( canEditMonitors ) row.Monitor = '<a href="?view=monitor&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&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>';
|
||||
row.Thumbnail = '<a href="?view=event&eid=' + eid + filterQuery + sortQuery + '&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();
|
||||
}
|
||||
|
|
|
@ -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') ?>";
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue