diff --git a/web/ajax/console.php b/web/ajax/console.php
index ff1f82c1b..61be3aa29 100644
--- a/web/ajax/console.php
+++ b/web/ajax/console.php
@@ -1,4 +1,23 @@
Username());
+ return;
+ }
+ $data = queryRequest();
+ ajaxResponse($data);
+ return;
+ }
+}
+
+// Handle legacy action-based requests
if ( canEdit('Monitors') ) {
switch ( $_REQUEST['action'] ) {
case 'sort' :
@@ -30,4 +49,364 @@ if ( canEdit('Monitors') ) {
}
ajaxError('Unrecognised action '.$_REQUEST['action'].' or insufficient permissions for user ' . $user->Username());
+
+//
+// FUNCTION DEFINITIONS
+//
+
+function queryRequest() {
+ global $user, $Servers;
+ require_once('includes/Monitor.php');
+ require_once('includes/Group_Monitor.php');
+
+ $data = array(
+ 'total' => 0,
+ 'totalNotFiltered' => 0,
+ 'rows' => array()
+ );
+
+ // Get pagination parameters
+ $offset = 0;
+ if (isset($_REQUEST['offset']) and ($_REQUEST['offset'] != 'NaN')) {
+ if ((!is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']))) {
+ ZM\Error('Invalid value for offset: ' . $_REQUEST['offset']);
+ } else {
+ $offset = $_REQUEST['offset'];
+ }
+ }
+
+ $limit = 0;
+ if (isset($_REQUEST['limit']) and ($_REQUEST['limit'] != 'NaN')) {
+ if ((!is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']))) {
+ ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']);
+ } else {
+ $limit = $_REQUEST['limit'];
+ }
+ }
+
+ // Get search parameter
+ $search = isset($_REQUEST['search']) ? $_REQUEST['search'] : '';
+
+ // Get sort parameters
+ $sort = isset($_REQUEST['sort']) ? $_REQUEST['sort'] : 'Sequence';
+ $order = isset($_REQUEST['order']) ? strtoupper($_REQUEST['order']) : 'ASC';
+
+ // Build monitor query with filters from session
+ zm_session_start();
+ $conditions = array();
+ $values = array();
+
+ // Store session filters for later use
+ $session_filters = array(
+ 'GroupId' => isset($_SESSION['GroupId']) ? $_SESSION['GroupId'] : null,
+ 'ServerId' => isset($_SESSION['ServerId']) ? $_SESSION['ServerId'] : null,
+ 'StorageId' => isset($_SESSION['StorageId']) ? $_SESSION['StorageId'] : null,
+ 'Capturing' => isset($_SESSION['Capturing']) ? $_SESSION['Capturing'] : null,
+ 'Analysing' => isset($_SESSION['Analysing']) ? $_SESSION['Analysing'] : null,
+ 'Recording' => isset($_SESSION['Recording']) ? $_SESSION['Recording'] : null,
+ 'Status' => isset($_SESSION['Status']) ? $_SESSION['Status'] : null,
+ 'MonitorName' => isset($_SESSION['MonitorName']) ? $_SESSION['MonitorName'] : null,
+ 'Source' => isset($_SESSION['Source']) ? $_SESSION['Source'] : null
+ );
+
+ session_write_close();
+
+ // Apply session filters to SQL
+ if ($session_filters['GroupId']) {
+ $GroupIds = is_array($session_filters['GroupId']) ? $session_filters['GroupId'] : array($session_filters['GroupId']);
+ $conditions[] = 'M.Id IN (SELECT MonitorId FROM Groups_Monitors WHERE GroupId IN (' . implode(',', array_fill(0, count($GroupIds), '?')) . '))';
+ $values = array_merge($values, $GroupIds);
+ }
+
+ foreach (array('ServerId','StorageId') as $filter) {
+ if ($session_filters[$filter]) {
+ $filter_values = is_array($session_filters[$filter]) ? $session_filters[$filter] : array($session_filters[$filter]);
+ if (count($filter_values)) {
+ $conditions[] = 'M.'.$filter.' IN (' . implode(',', array_fill(0, count($filter_values), '?')) . ')';
+ $values = array_merge($values, $filter_values);
+ }
+ }
+ }
+
+ foreach (array('Capturing','Analysing','Recording') as $filter) {
+ if ($session_filters[$filter]) {
+ $filter_values = is_array($session_filters[$filter]) ? $session_filters[$filter] : array($session_filters[$filter]);
+ if (count($filter_values)) {
+ $conditions[] = 'M.'.$filter.' IN (' . implode(',', array_fill(0, count($filter_values), '?')) . ')';
+ $values = array_merge($values, $filter_values);
+ }
+ }
+ }
+
+ if ($session_filters['Status']) {
+ $status_values = is_array($session_filters['Status']) ? $session_filters['Status'] : array($session_filters['Status']);
+ if (count($status_values)) {
+ $conditions[] = 'COALESCE(S.Status, IF(M.Type="WebSite","Running","NotRunning")) IN (' . implode(',', array_fill(0, count($status_values), '?')) . ')';
+ $values = array_merge($values, $status_values);
+ }
+ }
+
+ // Build SQL query
+ $sql = 'SELECT M.*, S.*, E.*
+ FROM Monitors AS M
+ LEFT JOIN Monitor_Status AS S ON S.MonitorId=M.Id
+ LEFT JOIN Event_Summaries AS E ON E.MonitorId=M.Id
+ WHERE M.`Deleted`=false';
+
+ if (count($conditions)) {
+ $sql .= ' AND ' . implode(' AND ', $conditions);
+ }
+
+ // Get total count before filtering
+ $monitors = dbFetchAll($sql, null, $values);
+ $unfiltered_monitors = array();
+ foreach ($monitors as $monitor) {
+ if (visibleMonitor($monitor['Id'])) {
+ $unfiltered_monitors[] = $monitor;
+ }
+ }
+ $data['totalNotFiltered'] = count($unfiltered_monitors);
+
+ // Apply search filter
+ $filtered_monitors = $unfiltered_monitors;
+ if ($search != '') {
+ $search_lower = strtolower($search);
+ $filtered_monitors = array_filter($unfiltered_monitors, function($monitor) use ($search_lower) {
+ // Search across common fields without creating Monitor object
+ return (
+ stripos($monitor['Name'], $search_lower) !== false ||
+ stripos($monitor['Function'], $search_lower) !== false ||
+ stripos($monitor['Path'], $search_lower) !== false ||
+ stripos($monitor['Device'], $search_lower) !== false ||
+ stripos($monitor['Host'], $search_lower) !== false ||
+ stripos($monitor['Id'], $search_lower) !== false ||
+ (isset($monitor['Status']) && stripos($monitor['Status'], $search_lower) !== false)
+ );
+ });
+ }
+
+ // Apply MonitorName and Source session filters
+ if ($session_filters['MonitorName']) {
+ $regexp = $session_filters['MonitorName'];
+ if (!strpos($regexp, '/')) $regexp = '/'.$regexp.'/i';
+ $filtered_monitors = array_filter($filtered_monitors, function($monitor) use ($regexp) {
+ return @preg_match($regexp, $monitor['Name']);
+ });
+ }
+
+ if ($session_filters['Source']) {
+ $regexp = $session_filters['Source'];
+ if (!preg_match("/^\/.+\/[a-z]*$/i", $regexp))
+ $regexp = '/'.$regexp.'/i';
+ $filtered_monitors = array_filter($filtered_monitors, function($monitor) use ($regexp) {
+ // Match against Path field directly instead of creating Monitor object
+ return (preg_match($regexp, $monitor['Path']) || preg_match($regexp, $monitor['Device']) || preg_match($regexp, $monitor['Host']));
+ });
+ }
+
+ $data['total'] = count($filtered_monitors);
+
+ // Sort monitors
+ usort($filtered_monitors, function($a, $b) use ($sort, $order) {
+ $aVal = isset($a[$sort]) ? $a[$sort] : '';
+ $bVal = isset($b[$sort]) ? $b[$sort] : '';
+
+ if (is_numeric($aVal) && is_numeric($bVal)) {
+ $result = $aVal - $bVal;
+ } else {
+ $result = strcasecmp($aVal, $bVal);
+ }
+
+ return $order == 'ASC' ? $result : -$result;
+ });
+
+ // Apply pagination
+ if ($limit > 0) {
+ $filtered_monitors = array_slice($filtered_monitors, $offset, $limit);
+ } else {
+ $filtered_monitors = array_slice($filtered_monitors, $offset);
+ }
+
+ // Get storage areas and servers
+ $storage_areas = ZM\Storage::find();
+ $StorageById = array();
+ foreach ($storage_areas as $S) {
+ $StorageById[$S->Id()] = $S;
+ }
+
+ $ServersById = array();
+ foreach ($Servers as $s) {
+ $ServersById[$s->Id()] = $s;
+ }
+
+ // Get group IDs for each monitor
+ $monitor_ids = array_map(function($m) { return $m['Id']; }, $filtered_monitors);
+ $group_ids_by_monitor_id = array();
+ if (count($monitor_ids)) {
+ foreach (ZM\Group_Monitor::find(array('MonitorId'=>$monitor_ids)) as $GM) {
+ if (!isset($group_ids_by_monitor_id[$GM->MonitorId()]))
+ $group_ids_by_monitor_id[$GM->MonitorId()] = array();
+ $group_ids_by_monitor_id[$GM->MonitorId()][] = $GM->GroupId();
+ }
+ }
+
+ // Process each monitor and build row data
+ $footer_totals = array(
+ 'monitor_count' => count($filtered_monitors),
+ 'total_bandwidth' => 0,
+ 'total_fps' => 0,
+ 'total_analysis_fps' => 0,
+ 'total_zones' => 0,
+ 'event_totals' => array(
+ 'Total' => array('events' => 0, 'diskspace' => 0),
+ 'Hour' => array('events' => 0, 'diskspace' => 0),
+ 'Day' => array('events' => 0, 'diskspace' => 0),
+ 'Week' => array('events' => 0, 'diskspace' => 0),
+ 'Month' => array('events' => 0, 'diskspace' => 0),
+ 'Archived' => array('events' => 0, 'diskspace' => 0)
+ )
+ );
+
+ foreach ($filtered_monitors as $monitor) {
+ $Monitor = new ZM\Monitor($monitor);
+ $Monitor->GroupIds(isset($group_ids_by_monitor_id[$Monitor->Id()]) ? $group_ids_by_monitor_id[$Monitor->Id()] : array());
+
+ // Accumulate footer totals
+ $footer_totals['total_bandwidth'] += isset($monitor['CaptureBandwidth']) ? $monitor['CaptureBandwidth'] : 0;
+ $footer_totals['total_fps'] += isset($monitor['CaptureFPS']) ? floatval($monitor['CaptureFPS']) : 0;
+ $footer_totals['total_analysis_fps'] += isset($monitor['AnalysisFPS']) ? floatval($monitor['AnalysisFPS']) : 0;
+ $footer_totals['total_zones'] += isset($monitor['ZoneCount']) ? intval($monitor['ZoneCount']) : 0;
+
+ foreach (array('Total', 'Hour', 'Day', 'Week', 'Month', 'Archived') as $period) {
+ $footer_totals['event_totals'][$period]['events'] += isset($monitor[$period.'Events']) ? intval($monitor[$period.'Events']) : 0;
+ $footer_totals['event_totals'][$period]['diskspace'] += isset($monitor[$period.'EventDiskSpace']) ? intval($monitor[$period.'EventDiskSpace']) : 0;
+ }
+
+ $row = array();
+ $row['Id'] = $monitor['Id'];
+ $row['Name'] = validHtmlStr($monitor['Name']);
+ $row['Function'] = $monitor['Function'];
+ $row['Enabled'] = $monitor['Enabled'];
+
+ // Status
+ if (!$monitor['Status']) {
+ if ($monitor['Type'] == 'WebSite')
+ $monitor['Status'] = 'Running';
+ else
+ $monitor['Status'] = 'NotRunning';
+ }
+ $row['Status'] = $monitor['Status'];
+
+ // Server
+ if (count($Servers)) {
+ $Server = isset($ServersById[$monitor['ServerId']]) ? $ServersById[$monitor['ServerId']] : new ZM\Server($monitor['ServerId']);
+ $row['Server'] = validHtmlStr($Server->Name());
+ $row['ServerId'] = $monitor['ServerId'];
+ }
+
+ // Source
+ $row['Source'] = validHtmlStr($Monitor->Source());
+ $row['Width'] = $Monitor->Width();
+ $row['Height'] = $Monitor->Height();
+
+ // Storage
+ if (isset($StorageById[$monitor['StorageId']])) {
+ $row['Storage'] = validHtmlStr($StorageById[$monitor['StorageId']]->Name());
+ } else if ($monitor['StorageId']) {
+ $row['Storage'] = 'Deleted '.$monitor['StorageId'].'';
+ } else {
+ $row['Storage'] = '';
+ }
+
+ // Event counts
+ $eventCounts = array('Total', 'Hour', 'Day', 'Week', 'Month', 'Archived');
+ foreach ($eventCounts as $period) {
+ $row[$period.'Events'] = (int)$monitor[$period.'Events'];
+ $row[$period.'EventDiskSpace'] = human_filesize($monitor[$period.'EventDiskSpace']);
+ }
+
+ // Zone count
+ $row['ZoneCount'] = $monitor['ZoneCount'];
+
+ // FPS and bandwidth
+ $row['CaptureFPS'] = isset($monitor['CaptureFPS']) ? $monitor['CaptureFPS'] : '0.00';
+ $row['AnalysisFPS'] = isset($monitor['AnalysisFPS']) ? $monitor['AnalysisFPS'] : '0.00';
+ $row['CaptureBandwidth'] = isset($monitor['CaptureBandwidth']) ? $monitor['CaptureBandwidth'] : 0;
+ $row['Analysing'] = isset($monitor['Analysing']) ? $monitor['Analysing'] : 'None';
+ $row['Recording'] = isset($monitor['Recording']) ? $monitor['Recording'] : 'None';
+ $row['ONVIF_Event_Listener'] = isset($monitor['ONVIF_Event_Listener']) ? $monitor['ONVIF_Event_Listener'] : 0;
+ $row['UpdatedOn'] = isset($monitor['UpdatedOn']) ? $monitor['UpdatedOn'] : '';
+ $row['Type'] = $monitor['Type'];
+ $row['Capturing'] = isset($monitor['Capturing']) ? $monitor['Capturing'] : 'None';
+
+ // Groups
+ if (canView('Groups')) {
+ $groups_html = implode('
',
+ array_map(function($group_id) {
+ $Group = ZM\Group::find_one(array('Id'=>$group_id));
+ if ($Group) {
+ $Groups = $Group->Parents();
+ array_push($Groups, $Group);
+ } else {
+ $Groups = array();
+ }
+ return implode(' > ', array_map(function($Group) {
+ if (canView('Stream')) {
+ return ''.validHtmlStr($Group->Name()).'';
+ } else {
+ return validHtmlStr($Group->Name());
+ }
+ }, $Groups));
+ }, $Monitor->GroupIds())
+ );
+ $row['Groups'] = $groups_html;
+ } else {
+ $row['Groups'] = '';
+ }
+
+ // Thumbnail
+ $row['Thumbnail'] = '';
+ if (ZM_WEB_LIST_THUMBS && ($monitor['Capturing'] != 'None') && canView('Stream')) {
+ $options = array();
+ $ratio_factor = $Monitor->ViewWidth() ? $Monitor->ViewHeight() / $Monitor->ViewWidth() : 1;
+ $options['width'] = ZM_WEB_LIST_THUMB_WIDTH;
+ $options['height'] = ZM_WEB_LIST_THUMB_HEIGHT ? ZM_WEB_LIST_THUMB_HEIGHT : ZM_WEB_LIST_THUMB_WIDTH*$ratio_factor;
+ $options['scale'] = $Monitor->ViewWidth() ? intval(100*ZM_WEB_LIST_THUMB_WIDTH / $Monitor->ViewWidth()) : 100;
+ $options['mode'] = 'jpeg';
+ $options['frames'] = 1;
+
+ $stillSrc = $Monitor->getStreamSrc($options);
+ $streamSrc = $Monitor->getStreamSrc(array('scale'=>($options['scale'] > 20 ? 100 : $options['scale']*5)));
+
+ $thmbWidth = ($options['width']) ? 'width:'.$options['width'].'px;' : '';
+ $thmbHeight = ($options['height']) ? 'height:'.$options['height'].'px;' : '';
+
+ $row['Thumbnail'] = '
| - - | - -' : '>') . $monitor['Id'] ?> | -ViewWidth() ? $Monitor->ViewHeight() / $Monitor->ViewWidth() : 1; - $options['width'] = ZM_WEB_LIST_THUMB_WIDTH; - $options['height'] = ZM_WEB_LIST_THUMB_HEIGHT ? ZM_WEB_LIST_THUMB_HEIGHT : ZM_WEB_LIST_THUMB_WIDTH*$ratio_factor; - $options['scale'] = $Monitor->ViewWidth() ? intval(100*ZM_WEB_LIST_THUMB_WIDTH / $Monitor->ViewWidth()) : 100; - $options['mode'] = 'jpeg'; - $options['frames'] = 1; - - $stillSrc = $Monitor->getStreamSrc($options); - $streamSrc = $Monitor->getStreamSrc(array('scale'=>($options['scale'] > 20 ? 100 : $options['scale']*5))); - - $thmbWidth = ( $options['width'] ) ? 'width:'.$options['width'].'px;' : ''; - $thmbHeight = ( $options['height'] ) ? 'height:'.$options['height'].'px;' : ''; - - $imgHTML = ''; - } -?> -
- lens
- ' : '>') . validHtmlStr($monitor['Name']) ?> - -
-
-',
- array_map(function($group_id){
- $Group = ZM\Group::find_one(array('Id'=>$group_id));
- if ( $Group ) {
- $Groups = $Group->Parents();
- array_push( $Groups, $Group );
- }
- return implode(' > ', array_map(function($Group){
- if (canView('Stream')) {
- return ''.validHtmlStr($Group->Name()).'';
- } else {
- return validHtmlStr($Group->Name());
- }
- }, $Groups ));
- }, $Monitor->GroupIds()));
- }
-?>
- |
-
-
-';
- } else {
- echo translate('Status'.$monitor['Status']).' '; - if ($monitor['Analysing'] != 'None') { - echo translate('Analysing') . ': '.translate($monitor['Analysing']).' '; - } - if ($monitor['Recording'] != 'None') { - echo translate('Recording') . ': '.translate($monitor['Recording']) . ($monitor['ONVIF_Event_Listener'] ? ' Use ONVIF' : "") . ' '; - } - ?> -
-';
- } # end if offline
- echo ' | '.PHP_EOL;
- if (count($Servers)) {
- $Server = isset($ServersById[$monitor['ServerId']]) ? $ServersById[$monitor['ServerId']] : new ZM\Server($monitor['ServerId']);
- echo ''.validHtmlStr($Server->Name()).' | '.PHP_EOL; - } - echo ''. makeLink( '?view=monitor&mid='.$monitor['Id'], ''.validHtmlStr($Monitor->Source()).'', $Monitor->canEdit());
- echo ' '.$Monitor->Width().'x'.$Monitor->Height(); - echo ' | ';
- if ($show_storage_areas) {
- echo ''. - (isset($StorageById[$monitor['StorageId']]) ? validHtmlStr($StorageById[$monitor['StorageId']]->Name()) : ($monitor['StorageId']?'Deleted '.$monitor['StorageId'].'' : '')).' | '.PHP_EOL; - } - - foreach (array_keys($eventCounts) as $i) { - echo '' : '') .
- (int)$monitor[$i.'Events'] . ' ' . human_filesize($monitor[$i.'EventDiskSpace']).' | '.PHP_EOL;
- }
- echo ''. makeLink('?view=zones&mid='.$monitor['Id'], $monitor['ZoneCount'], canView('Monitors')) .' | '.PHP_EOL; -?> -
| + + + | index) {
+ var cell = $j(eventCells[index]);
+ // Only update the th-inner div if it exists
+ var innerDiv = cell.find('.th-inner');
+ var target = innerDiv.length ? innerDiv : cell;
+
+ var link = target.find('a');
+ if (link.length) {
+ // Preserve the link but update the count
+ var newHtml = footer[period + 'Events'] + ' ' +
+ footer[period + 'EventDiskSpace'] + ' ';
+ link.html(newHtml);
+ } else {
+ target.html(footer[period + 'Events'] + '' +
+ footer[period + 'EventDiskSpace'] + ' ');
+ }
+ }
+ });
+
+ // Update zone count
+ updateCell('td.colZones, th.colZones', footer.total_zones);
+}
+
+// Called by bootstrap-table to retrieve monitor data
+function ajaxRequest(params) {
+ if (ajax) ajax.abort();
+ ajax = $j.ajax({
+ method: 'POST',
+ url: thisUrl + '?view=request&request=console&task=query',
+ data: params.data,
+ timeout: 0,
+ success: function(data) {
+ if (data.result == 'Error') {
+ alert(data.message);
+ return;
+ }
+ var rows = processRows(data.rows);
+ // Store monitors for function modal using original ID
+ rows.forEach(function(row) {
+ monitors[row._id] = row;
+ });
+
+ // rearrange the result into what bootstrap-table expects
+ params.success({total: data.total, totalNotFiltered: data.totalNotFiltered, rows: rows});
+
+ // Update footer with totals from response after table is rendered
+ if (data.footer) {
+ updateFooter(data.footer);
+ }
+ },
+ error: function(jqXHR) {
+ if (jqXHR.statusText != 'abort') {
+ console.log("error", jqXHR);
+ }
+ }
+ });
+}
+
+function processRows(rows) {
+ $j.each(rows, function(ndx, row) {
+ var mid = row.Id;
+
+ // Store original ID for later use
+ row._id = mid;
+
+ var stream_available = canView.Stream && (row.Type == 'WebSite' || (row.CaptureFPS && row.Capturing != 'None'));
+
+ // Determine status classes
+ var source_class = 'infoText';
+ var source_class_reason = '';
+ // FPS report interval: 60 seconds base + 30 seconds buffer for FPSReportInterval
+ var fps_report_seconds = 90;
+
+ if ((!row.Status || row.Status == 'NotRunning') && row.Type != 'WebSite') {
+ source_class = 'errorText';
+ source_class_reason = 'Not Running';
+ } else if (!row.UpdatedOn || (new Date(row.UpdatedOn).getTime() < Date.now() - fps_report_seconds * 1000)) {
+ source_class = 'errorText';
+ source_class_reason = 'Offline';
+ } else {
+ if (row.CaptureFPS == '0.00') {
+ source_class = 'errorText';
+ source_class_reason = 'No capture FPS';
+ } else if (!row.AnalysisFPS && row.Analysing != 'None') {
+ source_class = 'warnText';
+ source_class_reason = 'No analysis FPS';
+ }
+ }
+
+ var dot_class = source_class;
+ var dot_class_reason = source_class_reason;
+
+ // Format Id column
+ if (stream_available) {
+ row.Id = '' + mid + '';
+ } else {
+ row.Id = mid;
+ }
+
+ // Thumbnail goes in its own column if enabled
+ // (row.Thumbnail is already set from AJAX response)
+
+ // Format Name column with status indicator, link, and groups (no thumbnail)
+ var nameHtml = 'lens ';
+ if (stream_available) {
+ nameHtml += '' + row.Name + '';
+ } else {
+ nameHtml += row.Name;
+ }
+
+ // Add groups
+ if (row.Groups) {
+ nameHtml += '' + row.Groups + ' ';
+ }
+
+ row.Name = nameHtml;
+
+ // Format Function column with status and FPS info
+ var functionHtml = '';
+ if (!row.UpdatedOn || (new Date(row.UpdatedOn).getTime() < Date.now() - fps_report_seconds * 1000)) {
+ functionHtml = 'Offline'; + } else { + functionHtml = ''; + if (row.Analysing && row.Analysing != 'None') { + functionHtml += 'Analysing: ' + row.Analysing + ' '; + } + if (row.Recording && row.Recording != 'None') { + functionHtml += 'Recording: ' + row.Recording; + if (row.ONVIF_Event_Listener) { + functionHtml += ' Use ONVIF'; + } + functionHtml += ' '; + } + functionHtml += ' ';
+
+ var fps_string = '';
+ if (row.CaptureFPS) {
+ fps_string = row.CaptureFPS;
+ }
+ if (row.AnalysisFPS && row.Analysing != 'None') {
+ fps_string += '/' + row.AnalysisFPS;
+ }
+ if (fps_string) fps_string += ' fps';
+ if (row.CaptureBandwidth) {
+ fps_string += ' ' + row.CaptureBandwidth;
+ }
+ functionHtml += fps_string + ' ';
+ }
+ row.Function = functionHtml;
+
+ // Format Source column with link and dimensions
+ var sourceHtml = '';
+ if (canEdit.Monitors) {
+ sourceHtml = '' + row.Source + '';
+ } else {
+ sourceHtml = '' + row.Source + '';
+ }
+ sourceHtml += '' + row.Width + 'x' + row.Height; + row.Source = sourceHtml; + + // Format event count columns + var eventPeriods = ['Total', 'Hour', 'Day', 'Week', 'Month', 'Archived']; + eventPeriods.forEach(function(period) { + if (canView.Events) { + row[period + 'Events'] = '' + + row[period + 'Events'] + ' ' +
+ row[period + 'EventDiskSpace'] + ' ';
+ } else {
+ row[period + 'Events'] = row[period + 'Events'] + '' +
+ row[period + 'EventDiskSpace'] + ' ';
+ }
+ });
+
+ // Format Zones column
+ if (canView.Monitors) {
+ row.ZoneCount = '' + row.ZoneCount + '';
+ }
+ });
+
+ return rows;
+}
+
+function setButtonStates() {
+ const selections = table.bootstrapTable('getSelections');
+ const form = document.forms['monitorForm'];
+
+ if (selections && selections.length > 0) {
form.editBtn.disabled = false;
form.deleteBtn.disabled = false;
form.selectBtn.disabled = false;
@@ -44,73 +251,69 @@ function cloneMonitor(element) {
alert('Need create monitors privilege');
return;
}
- var form = element.form;
- var monitorId = -1;
- // get the value of the first checkbox
- for ( var i=0, len=form.elements.length; i < len; i++ ) {
- if (
- form.elements[i].type == "checkbox" &&
- form.elements[i].name == "markMids[]" &&
- form.elements[i].checked
- ) {
- monitorId = form.elements[i].value;
- break;
- }
- } // end foreach element
- if ( monitorId != -1 ) {
- window.location.assign('?view=monitor&dupId='+monitorId);
+ const selections = table.bootstrapTable('getSelections');
+ if (selections.length > 0) {
+ var monitorId = selections[0]._id;
+ window.location.assign('?view=monitor&dupId=' + monitorId);
} else {
alert('Please select a monitor to clone');
}
}
-function editMonitor( element ) {
- var form = element.form;
- var monitorIds = Array();
-
- for ( var i = 0; i < form.elements.length; i++ ) {
- if (
- form.elements[i].type == "checkbox" &&
- form.elements[i].name == "markMids[]" &&
- form.elements[i].checked
- ) {
- monitorIds.push( form.elements[i].value );
- }
- } // end foreach checkboxes
- if ( monitorIds.length == 1 ) {
- window.location.assign('?view=monitor&mid='+monitorIds[0]);
- } else if ( monitorIds.length > 1 ) {
- window.location.assign( '?view=monitors&'+(monitorIds.map(function(mid) {
- return 'mids[]='+mid;
+function editMonitor(element) {
+ const selections = table.bootstrapTable('getSelections');
+ if (selections.length == 0) return;
+
+ var monitorIds = selections.map(function(sel) {
+ return sel._id;
+ });
+
+ if (monitorIds.length == 1) {
+ window.location.assign('?view=monitor&mid=' + monitorIds[0]);
+ } else if (monitorIds.length > 1) {
+ window.location.assign('?view=monitors&' + (monitorIds.map(function(mid) {
+ return 'mids[]=' + mid;
}).join('&')));
}
}
-function deleteMonitor( element ) {
+function deleteMonitor(element) {
if (confirm('Deleting a monitor only marks it as deleted. Events will age out. If you want them to be immediately removed, please delete them first.\nAre you sure you wish to delete?')) {
const form = element.form;
form.elements['action'].value = 'delete';
+
+ // Get selected monitor IDs and add them to the form
+ const selections = table.bootstrapTable('getSelections');
+ selections.forEach(function(sel) {
+ var input = document.createElement('input');
+ input.type = 'hidden';
+ input.name = 'markMids[]';
+ input.value = sel._id;
+ form.appendChild(input);
+ });
+
form.submit();
}
}
function selectMonitor(element) {
- var form = element.form;
- var url = thisUrl+'?view=console';
- for ( var i = 0; i < form.elements.length; i++ ) {
- if (
- form.elements[i].type == 'checkbox' &&
- form.elements[i].name == 'markMids[]' &&
- form.elements[i].checked
- ) {
- url += '&MonitorId[]='+form.elements[i].value;
- }
- }
+ const selections = table.bootstrapTable('getSelections');
+ var url = thisUrl + '?view=console';
+
+ selections.forEach(function(sel) {
+ url += '&MonitorId[]=' + sel._id;
+ });
+
window.location.replace(url);
}
function reloadWindow() {
- window.location.replace( thisUrl );
+ // Use table refresh instead of full page reload
+ if (table && table.length) {
+ table.bootstrapTable('refresh');
+ } else {
+ window.location.replace(thisUrl);
+ }
}
// Manage the the Function modal and its buttons
@@ -181,10 +384,46 @@ function manageFunctionModal(evt) {
$j('#modalFunction').modal('show');
} // end function manageFunctionModal
+// Called when monitor filters change - refreshes table via AJAX instead of full page reload
+function monitorFilterOnChange() {
+ // On console view with bootstrap-table, just refresh the table
+ if (typeof table !== 'undefined' && table.length) {
+ table.bootstrapTable('refresh');
+ } else {
+ // Fall back to full page reload on other views
+ submitThisForm();
+ }
+}
+
function initPage() {
+ // Init the bootstrap-table
+ table.bootstrapTable({icons: icons});
+
+ // Enable or disable buttons based on current selection and user rights
+ table.on('check.bs.table uncheck.bs.table check-all.bs.table uncheck-all.bs.table',
+ function() {
+ const selections = table.bootstrapTable('getSelections');
+ const form = document.forms['monitorForm'];
+
+ if (selections.length > 0) {
+ form.editBtn.disabled = false;
+ form.deleteBtn.disabled = false;
+ form.selectBtn.disabled = false;
+ form.cloneBtn.disabled = false;
+ } else {
+ form.editBtn.disabled = true;
+ form.deleteBtn.disabled = true;
+ form.selectBtn.disabled = true;
+ form.cloneBtn.disabled = true;
+ }
+ }
+ );
+
+ // Setup automatic refresh with table refresh instead of page reload
if (consoleRefreshTimeout > 0) {
setInterval(reloadWindow, consoleRefreshTimeout);
}
+
if ( showDonatePopup ) {
$j.getJSON(thisUrl + '?request=modal&modal=donate')
.done(function(data) {
@@ -199,17 +438,21 @@ function initPage() {
.fail(logAjaxFail);
}
+ // Setup the thumbnail video animation after table loads
+ table.on('post-body.bs.table', function() {
+ if (!isMobile()) initThumbAnimation();
+ $j('.functionLnk').click(manageFunctionModal);
+ });
- // Setup the thumbnail video animation
- if (!isMobile()) initThumbAnimation();
-
- $j('.functionLnk').click(manageFunctionModal);
-
- // Makes table sortable
+ // Makes table sortable - disabled by default, enabled by Sort button
+ // Note: This may need adjustment for bootstrap-table compatibility
$j('#consoleTableBody').sortable({
disabled: true,
update: applySort,
axis: 'Y'} );
+
+ // Make the table visible after initialization
+ table.show();
} // end function initPage
function sortMonitors(button) {
diff --git a/web/skins/classic/views/js/console.js.php b/web/skins/classic/views/js/console.js.php
index 736d1611f..424ae4372 100644
--- a/web/skins/classic/views/js/console.js.php
+++ b/web/skins/classic/views/js/console.js.php
@@ -11,22 +11,6 @@ if ( canEdit('System') && ZM_DYN_SHOW_DONATE_REMINDER ) {
}
?>
var showDonatePopup = ;
-var monitors = new Array();
-
- monitors[Id() ?>] = {
- 'Id': Id() ?>,
- 'Name': 'Name() ?>',
- 'ViewWidth': ViewWidth() ?>,
- 'ViewHeight':ViewHeight() ?>,
- 'Url': 'UrlToIndex( ZM_MIN_STREAMING_PORT ? ($monitor->Id() + ZM_MIN_STREAMING_PORT) : '') ?>',
- 'Type': 'Type() ?>',
- 'Function': 'Function() ?>',
- 'Enabled': 'Enabled() ?>',
- 'DecodingEnabled': 'DecodingEnabled() ?>'
-};
-
+
+var ZM_WEB_EVENTS_VIEW = '';
+
|