Merge branch 'master' of ../ZoneMinder.connortechnology

pull/3659/head
Isaac Connor 2023-01-16 17:23:52 -05:00
commit 8fa1d98a7b
38 changed files with 477 additions and 398 deletions

2
.github/FUNDING.yml vendored
View File

@ -1,6 +1,6 @@
# These are supported funding model platforms # These are supported funding model platforms
github: [connortechnology,pliablepixels] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] github: [zoneminder,connortechnology,pliablepixels] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: zoneminder # Replace with a single Patreon username patreon: zoneminder # Replace with a single Patreon username
open_collective: zoneminder # Replace with a single Open Collective username open_collective: zoneminder # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username ko_fi: # Replace with a single Ko-fi username

View File

@ -30,6 +30,7 @@ To use this repository instead of the official Ubuntu repository, enter the foll
**Step 3:** Install Zoneminder **Step 3:** Install Zoneminder
:: ::
sudo apt install -y zoneminder sudo apt install -y zoneminder

View File

@ -1585,6 +1585,14 @@ our @options = (
}, },
category => 'web', category => 'web',
}, },
{
name => 'ZM_WEB_NAVBAR_STICKY',
default => '1',
description => 'Are navbars and button bars sticky?',
help => 'Do navbars and button bars remain fixed in place or scroll with the page content? Please note that this will only affect large browsers. Due to our responsive layout on narrow screens everything will become unsticky.',
type => $types{boolean},
category => 'web',
},
{ {
name => 'ZM_WEB_TITLE', name => 'ZM_WEB_TITLE',
default => 'ZoneMinder', default => 'ZoneMinder',

View File

@ -227,7 +227,7 @@ int FfmpegCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
} }
return -1; return -1;
} }
if ((packet->pts < 0) and (lastPTS >=0)) { if ((packet->pts < 0) and (packet->pts != AV_NOPTS_VALUE) and (lastPTS >= 0)) {
// 32-bit wrap around? // 32-bit wrap around?
Info("Suspected 32bit wraparound in input pts. %" PRId64, packet->pts); Info("Suspected 32bit wraparound in input pts. %" PRId64, packet->pts);
return -1; return -1;

View File

@ -13,7 +13,7 @@
/* Function to send the contents of a file. Will use sendfile or fall back to reading/writing */ /* Function to send the contents of a file. Will use sendfile or fall back to reading/writing */
ssize_t zm_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { ssize_t zm_sendfile(int out_fd, int in_fd, off_t *offset, ssize_t size) {
#ifdef HAVE_SENDFILE4_SUPPORT #ifdef HAVE_SENDFILE4_SUPPORT
ssize_t err = sendfile(out_fd, in_fd, offset, size); ssize_t err = sendfile(out_fd, in_fd, offset, size);
if (err < 0) { if (err < 0) {

View File

@ -983,10 +983,10 @@ int VideoStore::writePacket(const std::shared_ptr<ZMPacket> &zm_pkt) {
} // end while } // end while
if (have_out_of_order) { if (have_out_of_order) {
AVPacket *p = ((*rit)->packet).get();
if (rit == queue.rend()) { if (rit == queue.rend()) {
Warning("Unable to re-order packet, packet dts is %" PRId64, p->dts); Debug(1, "Unable to re-order packet, packet dts is %" PRId64, av_pkt->dts);
} else { } else {
AVPacket *p = ((*rit)->packet).get();
Debug(1, "Found out of order packet, inserting after %" PRId64, p->dts); Debug(1, "Found out of order packet, inserting after %" PRId64, p->dts);
} }
queue.insert(rit.base(), zm_pkt); queue.insert(rit.base(), zm_pkt);

View File

@ -34,15 +34,15 @@ var Server = function() {
} }
}, },
{ {
key: 'UrlToZMS', key: 'urlToZMS',
value: function UrlToZMS() { value: function urlToZMS() {
const port = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; const port = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
return this.Protocol + '://' + this.Hostname + (port ? ':' + port : '') + (this.PathToZMS && this.PathToZMS != 'null' ? this.PathToZMS : ''); return this.Protocol + '://' + this.Hostname + (port ? ':' + port : '') + (this.PathToZMS && this.PathToZMS != 'null' ? this.PathToZMS : '');
} }
}, },
{ {
key: 'UrlToApi', key: 'urlToApi',
value: function UrlToApi() { value: function urlToApi() {
const port = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; const port = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
return this.Protocol + '://' + this.Hostname + (port ? ':' + port : '') + (this.PathToApi && this.PathToApi != 'null' ? this.PathToApi : ''); return this.Protocol + '://' + this.Hostname + (port ? ':' + port : '') + (this.PathToApi && this.PathToApi != 'null' ? this.PathToApi : '');
} }

View File

@ -50,11 +50,24 @@
.material-icons.md-18 { font-size: 18px; } .material-icons.md-18 { font-size: 18px; }
.material-icons.md-36 { font-size: 36px; } .material-icons.md-36 { font-size: 36px; }
html,
body { body {
font-family: "Open Sans", Verdana, Arial, Helvetica, sans-serif; height: 100%;
font-size: 13px; margin: 0;
font-weight: 300; }
text-align: center;
body {
font-family: "Open Sans", Verdana, Arial, Helvetica, sans-serif;
font-size: 13px;
font-weight: 300;
text-align: center;
}
body.sticky {
display: flex;
flex-flow: column nowrap;
flex-direction: column;
height: 100%;
} }
h1 { h1 {
@ -220,7 +233,24 @@ ul.tabList li.active {
} }
ul.tabList li.active a { ul.tabList li.active a {
font-weight: bold; font-weight: bold;
}
#content {
width: 100%;
margin: 0 auto 8px auto;
line-height: 130%;
text-align: center;
clear: both;
}
body.sticky #content {
/*
flex: 1 1 auto;
display: flex;
flex-flow: column;
*/
overflow: hidden;
height: 100%;
} }
/* /*
@ -454,6 +484,12 @@ th.table-th-sort-rev span.table-th-sort-span {
#page { #page {
width: 100%; width: 100%;
} }
body.sticky #page {
flex: 1 1 auto;
display: flex;
flex-flow: column;
overflow: hidden;
}
#header { #header {
width: 100%; width: 100%;
@ -505,13 +541,6 @@ th.table-th-sort-rev span.table-th-sort-span {
margin-left: 4px; margin-left: 4px;
} }
#content {
width: 100%;
margin: 0 auto 8px auto;
line-height: 130%;
text-align: center;
clear: both;
}
#content p { #content p {
margin-top: 4px; margin-top: 4px;
@ -629,10 +658,6 @@ color:#ffa801;
border:none; border:none;
} }
.container-fluid {
position: relative;
}
.sidebar { .sidebar {
position: absolute; position: absolute;
top: 0; top: 0;

View File

@ -114,3 +114,13 @@
.SourceFilter input { .SourceFilter input {
padding: 3px 5px 4px 5px; padding: 3px 5px 4px 5px;
} }
form {
display: flex;
flex-flow: column nowrap;
height: 100%;
flex: 1 1 auto;
}
#monitorList {
flex: 1 1 auto;
overflow-y: scroll;
}

View File

@ -7,3 +7,7 @@
text-align: center; text-align: center;
} }
#controls {
height: 100%;
overflow-y: auto;
}

View File

@ -88,3 +88,7 @@ min-width: 500px;
clear: both; clear: both;
visibility: hidden; visibility: hidden;
} }
#contentForm {
overflow-y: auto;
height: 100%;
}

View File

@ -31,3 +31,6 @@ th[data-field="DateTime"] {
.search .form-control { .search .form-control {
font-size: 100%; font-size: 100%;
} }
#logsTable {
overflow-y: auto;
}

View File

@ -73,3 +73,8 @@ tr td input[type="radio"] {
padding-right: 1rem; padding-right: 1rem;
text-align: right; text-align: right;
} }
#monitor {
overflow-y: auto;
height: 100%;
}

View File

@ -8,6 +8,10 @@
*/ */
} }
#monitors {
height: 100%;
overflow-y: auto;
}
#monitors:after { #monitors:after {
content: "."; content: ".";
display: block; display: block;

View File

@ -41,6 +41,7 @@ min-width: 0;
position:relative; position:relative;
width:100%; width:100%;
height:100%; height:100%;
overflow-y: auto;
} }
input[type=range]::-ms-tooltip { input[type=range]::-ms-tooltip {
display: none; display: none;

View File

@ -25,6 +25,15 @@ input.large {
text-align: left; text-align: left;
} }
#sidebar {
overflow-y: auto;
}
#options {
overflow-y: auto;
padding-top: 15px;
height: 100%;
}
#options div.col-md { #options div.col-md {
text-align: left; text-align: left;
} }
@ -60,9 +69,6 @@ input[name="newStorage[Url]"] {
#options label { #options label {
font-weight: bold; font-weight: bold;
} }
#options {
font-size:90%;
}
#options .help-block { #options .help-block {
margin-top: 0; margin-top: 0;
@ -76,3 +82,14 @@ input[name="newStorage[Url]"] {
#options .col-md { #options .col-md {
text-align: left; text-align: left;
} }
#contentButtons {
position: absolute;
top: 0;
right: 2rem;
}
form {
position: relative;
height: 100%;
padding-top: 4rem;
}

View File

@ -10,3 +10,8 @@
.colZeroSize { .colZeroSize {
text-align: left; text-align: left;
} }
#results {
height: 100%;
overflow-y: auto;
}

View File

@ -1,5 +1,4 @@
#content { #content {
margin: 0 15px;
} }
.Name { .Name {
@ -15,3 +14,8 @@
.Description textarea { .Description textarea {
width: 100%; width: 100%;
} }
#video {
overflow-y: auto;
height: 100%;
}

View File

@ -4,3 +4,8 @@ th[data-field="CreatedOn"],
th[data-field="Name"] { th[data-field="Name"] {
text-align: left; text-align: left;
} }
#snapshots {
height: 100%;
overflow-y: auto;
}

View File

@ -30,15 +30,10 @@ function xhtmlHeaders($file, $title) {
# This idea is that we always include the classic css files, # This idea is that we always include the classic css files,
# and then any different skin only needs to contain things that are different. # and then any different skin only needs to contain things that are different.
$baseCssPhpFile = getSkinFile('css/base/skin.css.php'); $baseCssPhpFile = getSkinFile('css/base/skin.css.php');
$skinCssPhpFile = getSkinFile('css/'.$css.'/skin.css.php'); $skinCssPhpFile = getSkinFile('css/'.$css.'/skin.css.php');
$basename = basename($file, '.php'); $basename = basename($file, '.php');
$baseViewCssPhpFile = getSkinFile('/css/base/views/'.$basename.'.css.php');
$viewCssPhpFile = getSkinFile('/css/'.$css.'/views/'.$basename.'.css.php');
function output_link_if_exists($files, $cache_bust=true) { function output_link_if_exists($files, $cache_bust=true) {
global $skin; global $skin;
$html = array(); $html = array();
@ -140,12 +135,15 @@ if ( $css != 'base' )
?> ?>
<style> <style>
<?php <?php
if ( $baseViewCssPhpFile ) { $baseCssPhpFile = getSkinFile('css/base/skin.css.php');
require_once($baseViewCssPhpFile); if ($baseCssPhpFile) require_once($baseCssPhpFile);
} $skinCssPhpFile = getSkinFile('css/'.$css.'/skin.css.php');
if ( $viewCssPhpFile ) { if ($skinCssPhpFile) require_once($baseCssPhpFile);
require_once($viewCssPhpFile);
} $baseViewCssPhpFile = getSkinFile('/css/base/views/'.$basename.'.css.php');
if ($baseViewCssPhpFile) require_once($baseViewCssPhpFile);
$viewCssPhpFile = getSkinFile('/css/'.$css.'/views/'.$basename.'.css.php');
if ($viewCssPhpFile) require_once($viewCssPhpFile);
?> ?>
</style> </style>
@ -157,7 +155,7 @@ if ( $css != 'base' )
// Outputs an opening body tag, and any additional content that should go at the very top, like warnings and error messages. // Outputs an opening body tag, and any additional content that should go at the very top, like warnings and error messages.
function getBodyTopHTML() { function getBodyTopHTML() {
echo ' echo '
<body> <body'.((defined('ZM_WEB_NAVBAR_STICKY') and ZM_WEB_NAVBAR_STICKY) ? ' class="sticky"' : '').'>
<noscript> <noscript>
<div style="background-color:red;color:white;font-size:x-large;"> <div style="background-color:red;color:white;font-size:x-large;">
'. validHtmlStr(ZM_WEB_TITLE) .' requires Javascript. Please enable Javascript in your browser for this site. '. validHtmlStr(ZM_WEB_TITLE) .' requires Javascript. Please enable Javascript in your browser for this site.
@ -248,8 +246,10 @@ function getNormalNavBarHTML($running, $user, $bandwidth_options, $view, $skin)
</div> </div>
</nav><!-- End First Navbar --> </nav><!-- End First Navbar -->
<nav class="navbar navbar-expand-md justify-content-center" id="navbar-two"> <nav class="navbar navbar-expand-md justify-content-center" id="navbar-two"
<div class="container-fluid" id="panel"<?php echo ( isset($_COOKIE['zmHeaderFlip']) and $_COOKIE['zmHeaderFlip'] == 'down' ) ? 'style="display:none;"' : '' ?>> <?php echo ( isset($_COOKIE['zmHeaderFlip']) and $_COOKIE['zmHeaderFlip'] == 'down' ) ? 'style="display:none;"' : '' ?>
>
<div class="container-fluid" id="panel" >
<?php <?php
if ( (!ZM_OPT_USE_AUTH) or $user ) { if ( (!ZM_OPT_USE_AUTH) or $user ) {

View File

@ -275,8 +275,8 @@ if ( currentView != 'none' && currentView != 'login' ) {
reminderClickFunction(); reminderClickFunction();
// Manage the widget bar minimize chevron // Manage the widget bar minimize chevron
$j("#flip").click(function() { $j("#flip").click(function() {
$j("#panel").slideToggle("slow"); $j("#navbar-two").slideToggle("slow");
var flip = $j("#flip"); const flip = $j("#flip");
if ( flip.html() == 'keyboard_arrow_up' ) { if ( flip.html() == 'keyboard_arrow_up' ) {
flip.html('keyboard_arrow_down'); flip.html('keyboard_arrow_down');
setCookie('zmHeaderFlip', 'down', 3600); setCookie('zmHeaderFlip', 'down', 3600);

View File

@ -39,7 +39,7 @@ const cancelString = '<?php echo translate('Cancel') ?>';
try to avoid using PHP_SELF but here I try to replace everything after '.php'. */ ?> try to avoid using PHP_SELF but here I try to replace everything after '.php'. */ ?>
const thisUrl = '<?php echo ZM_BASE_URL.preg_replace('/\.php.*$/i', '.php', $_SERVER['PHP_SELF']) ?>'; const thisUrl = '<?php echo ZM_BASE_URL.preg_replace('/\.php.*$/i', '.php', $_SERVER['PHP_SELF']) ?>';
const skinPath = '<?php echo ZM_SKIN_PATH ?>'; const skinPath = '<?php echo ZM_SKIN_PATH ?>';
const serverId = '<?php echo defined('ZM_SERVER_ID') ? ZM_SERVER_ID : '' ?>'; const serverId = <?php echo defined('ZM_SERVER_ID') ? ZM_SERVER_ID : '0' ?>;
const Servers = []; const Servers = [];
<?php <?php
// Fall back to get Server paths, etc when no using multi-server mode // Fall back to get Server paths, etc when no using multi-server mode

View File

@ -0,0 +1,98 @@
<?php
//
// ZoneMinder web options view file, $Date$, $Revision$
// Copyright (C) 2001-2008 Philip Coombes
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
if (!canView('System')) {
$view = 'error';
return;
}
$canEdit = canEdit('System');
if ((!defined('ZM_OPT_USE_API')) or ZM_OPT_USE_API != '1') {
echo '<div class="errorText">APIs are disabled. To enable, please turn on OPT_USE_API in Options->System</div>';
return;
}
?>
<form name="userForm" method="post" action="?">
<button class="float-left" type="submit" name="updateSelected" id="updateSelected"><?php echo translate('Update')?></button>
<button class="float-left" type="button" id="btnNewToken"><?php echo translate('New Token')?></button>
<button class="btn-danger float-right" type="submit" name="revokeAllTokens" id="revokeAllTokens"><?php echo translate('RevokeAllTokens')?></button>
<br/>
<?php
function revokeAllTokens() {
$minTokenTime = time();
dbQuery('UPDATE `Users` SET `TokenMinExpiry`=?', array($minTokenTime));
echo '<span class="timedSuccessBox">'.translate('AllTokensRevoked').'</span>';
}
function updateSelected() {
# Turn them all off, then selectively turn the checked ones back on
dbQuery('UPDATE `Users` SET `APIEnabled`=0');
if (isset($_REQUEST['tokenUids'])) {
$minTime = time();
foreach ($_REQUEST['tokenUids'] as $markUid) {
dbQuery('UPDATE `Users` SET `TokenMinExpiry`=? WHERE `Id`=?', array($minTime, $markUid));
}
}
if (isset($_REQUEST['apiUids'])) {
foreach ($_REQUEST['apiUids'] as $markUid) {
dbQuery('UPDATE `Users` SET `APIEnabled`=1 WHERE `Id`=?', array($markUid));
}
}
echo '<span class="timedSuccessBox">'.translate('Updated').'</span>';
}
if (array_key_exists('revokeAllTokens', $_POST)) {
revokeAllTokens();
}
if (array_key_exists('updateSelected', $_POST)) {
updateSelected();
}
?>
<br/><br/>
<input type="hidden" name="view" value="<?php echo $view ?>"/>
<input type="hidden" name="tab" value="<?php echo $tab ?>"/>
<input type="hidden" name="action" value="delete"/>
<table id="contentTable" class="table table-striped">
<thead class="thead-highlight">
<tr>
<th class="colUsername"><?php echo translate('Username') ?></th>
<th class="colMark"><?php echo translate('Revoke Token') ?></th>
<th class="colMark"><?php echo translate('API Enabled') ?></th>
</tr>
</thead>
<tbody>
<?php
foreach (ZM\User::find([], ['order'=>'Username']) as $u) {
?>
<tr>
<td class="colUsername"><?php echo validHtmlStr($u->Username()) ?></td>
<td class="colMark"><input type="checkbox" name="tokenUids[]" value="<?php echo $u->Id() ?>" /></td>
<td class="colMark"><input type="checkbox" name="apiUids[]" value="<?php echo $u->Id() ?>" <?php echo $u->APIEnabled()?'checked':''?> /></td>
</tr>
<?php
}
?>
</tbody>
</table>
</form>

View File

@ -164,8 +164,8 @@ if ( $show_storage_areas ) $left_columns += 1;
xhtmlHeaders(__FILE__, translate('Console')); xhtmlHeaders(__FILE__, translate('Console'));
getBodyTopHTML(); getBodyTopHTML();
?> echo $navbar ?>
<?php echo $navbar ?> <div id="page">
<div id="content"> <div id="content">
<form name="monitorForm" method="post" action="?view=<?php echo $view; ?>"> <form name="monitorForm" method="post" action="?view=<?php echo $view; ?>">
<input type="hidden" name="action" value=""/> <input type="hidden" name="action" value=""/>
@ -174,7 +174,7 @@ getBodyTopHTML();
<?php echo $filterbar ?> <?php echo $filterbar ?>
</div> </div>
<div class="container-fluid pt-2"> <div class="container-fluid pt-2" id="contentButtons">
<div class="statusBreakdown float-left"> <div class="statusBreakdown float-left">
<?php <?php
$html = ''; $html = '';
@ -213,10 +213,11 @@ getBodyTopHTML();
</button> </button>
&nbsp;<a href="#"><i id="fbflip" class="material-icons md-18">keyboard_arrow_<?php echo ( isset($_COOKIE['zmFilterBarFlip']) and $_COOKIE['zmFilterBarFlip'] == 'down') ? 'down' : 'up' ?></i></a> &nbsp;<a href="#"><i id="fbflip" class="material-icons md-18">keyboard_arrow_<?php echo ( isset($_COOKIE['zmFilterBarFlip']) and $_COOKIE['zmFilterBarFlip'] == 'down') ? 'down' : 'up' ?></i></a>
</div><!-- contentButtons -->
<?php <?php
ob_start(); ob_start();
?> ?>
<div class="table-responsive-sm pt-2"> <div class="container-fluid table-responsive-sm pt-2" id="monitorList">
<table class="table table-striped table-hover table-condensed consoleTable"> <table class="table table-striped table-hover table-condensed consoleTable">
<thead class="thead-highlight"> <thead class="thead-highlight">
<tr> <tr>
@ -449,10 +450,10 @@ for ($monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1) {
</tr> </tr>
</tfoot> </tfoot>
</table> </table>
</div> </div><!-- content table responsive div -->
</div>
</form> </form>
</div><!--content--> </div><!--content-->
</div><!--page-->
<?php <?php
xhtmlFooter(); xhtmlFooter();
?> ?>

View File

@ -28,9 +28,9 @@ $controls = dbFetchAll('SELECT * FROM Controls ORDER BY Name');
$focusWindow = true; $focusWindow = true;
xhtmlHeaders(__FILE__, translate('ControlCaps')); xhtmlHeaders(__FILE__, translate('ControlCaps'));
getBodyTopHTML();
echo getNavBarHTML();
?> ?>
<body>
<?php echo getNavBarHTML() ?>
<div id="page"> <div id="page">
<!-- Toolbar button placement and styling handled by bootstrap-tables --> <!-- Toolbar button placement and styling handled by bootstrap-tables -->
@ -43,6 +43,7 @@ xhtmlHeaders(__FILE__, translate('ControlCaps'));
</div> </div>
<div id="content" class="table-responsive-sm"> <div id="content" class="table-responsive-sm">
<div id="controls">
<table <table
id="controlTable" id="controlTable"
data-locale="<?php echo i18n() ?>" data-locale="<?php echo i18n() ?>"
@ -95,6 +96,7 @@ foreach( $controls as $control ) {
?> ?>
</tbody> </tbody>
</table> </table>
</div><!--controls-->
</div> </div>
</div> </div>
<?php xhtmlFooter() ?> <?php xhtmlFooter() ?>

View File

@ -42,8 +42,8 @@ foreach ( $Groups as $id=>$Group ) {
$max_depth = $Group->depth(); $max_depth = $Group->depth();
} }
xhtmlHeaders(__FILE__, translate('Groups')); xhtmlHeaders(__FILE__, translate('Groups'));
getBodyTopHTML();
?> ?>
<body>
<div id="page"> <div id="page">
<?php echo $navbar = getNavBarHTML(); ?> <?php echo $navbar = getNavBarHTML(); ?>
<div id="content"> <div id="content">

View File

@ -1,12 +1,5 @@
var form = $j('#monitorPresetForm');
function submitPreset( element ) {
form.target = opener.name;
form.view.value = 'monitor';
form.submit();
}
function configureButtons() { function configureButtons() {
const form = document.getElementById('monitorPresetForm');
form.saveBtn.disabled = (form.preset.selectedIndex==0); form.saveBtn.disabled = (form.preset.selectedIndex==0);
} }

View File

@ -86,7 +86,7 @@ function findFrameByTime(arr, time) {
//console.log(keys); //console.log(keys);
//console.log(keys[start]); //console.log(keys[start]);
// Iterate while start not meets end // Iterate while start not meets end
//console.log("Looking for "+ time+ "start: " + start + ' end ' + end, arr[keys[start]]); //console.log("Looking for "+ time+ "start: " + start + ' end ' + end, arr[keys[start]]);
while ((start <= end)) { while ((start <= end)) {
//&& arr[keys[start]] && (arr[keys[start]].TimeStampSecs <= time) && (arr[keys[end]].NextTimeStampSecs >= time)) { //&& arr[keys[start]] && (arr[keys[start]].TimeStampSecs <= time) && (arr[keys[end]].NextTimeStampSecs >= time)) {
// Find the mid index // Find the mid index
@ -101,7 +101,7 @@ function findFrameByTime(arr, time) {
(!frame.NextTimeStampSecs) || // only if event.EndTime is null (!frame.NextTimeStampSecs) || // only if event.EndTime is null
(frame.NextTimeStampSecs > time) (frame.NextTimeStampSecs > time)
) )
) { ) {
//console.log("Found it at ", frame); //console.log("Found it at ", frame);
return frame; return frame;
@ -172,21 +172,18 @@ function getFrame(monId, time, last_Frame) {
} }
} }
if (!Event) return; if (!Event) return;
let Frame = null;
if (!Event.FramesById) { if (!Event.FramesById) {
console.log('No FramesById for event ', Event.Id); console.log('No FramesById for event ', Event.Id);
load_Frames(Event).then(function() { load_Frames([Event]).then(function() {
if (!Event.FramesById) { if (!Event.FramesById) {
console.log("No FramesById after load_Frames!", Event); console.log("No FramesById after load_Frames!", Event);
} }
let Frame = null; let Frame = findFrameByTime(Event.FramesById, time);
Frame = findFrameByTime(Event.FramesById, time); return Frame;
console.log('Frame', Frame, time);
}, function(Error) { }, function(Error) {
console.log(Error); console.log(Error);
}); });
return; return;
} }
@ -194,7 +191,7 @@ function getFrame(monId, time, last_Frame) {
// Frames are sorted in descreasing order (or not sorted). // Frames are sorted in descreasing order (or not sorted).
// This is likely not efficient. Would be better to start at the last frame viewed, see if it is still relevant // This is likely not efficient. Would be better to start at the last frame viewed, see if it is still relevant
// Then move forward or backwards as appropriate // Then move forward or backwards as appropriate
Frame = findFrameByTime(Event.FramesById, time); let Frame = findFrameByTime(Event.FramesById, time);
if (!Frame) { if (!Frame) {
console.log("Didn't find frame by binary search"); console.log("Didn't find frame by binary search");
for (const frame_id in Event.FramesById) { for (const frame_id in Event.FramesById) {
@ -223,30 +220,30 @@ function getFrame(monId, time, last_Frame) {
// time is seconds since epoch // time is seconds since epoch
function getImageSource(monId, time) { function getImageSource(monId, time) {
if ( liveMode == 1 ) { if (liveMode == 1) {
var new_url = monitorImageObject[monId].src.replace( let new_url = monitorImageObject[monId].src.replace(
/rand=\d+/i, /rand=\d+/i,
'rand='+Math.floor(Math.random() * 1000000) 'rand='+Math.floor(Math.random() * 1000000)
); );
if ( auth_hash ) { if (auth_hash) {
// update auth hash // update auth hash
new_url = new_url.replace(/auth=[a-z0-9]+/i, 'auth='+auth_hash); new_url = new_url.replace(/auth=[a-z0-9]+/i, 'auth='+auth_hash);
} }
return new_url; return new_url;
} }
var frame_id; let frame_id;
var Frame = getFrame(monId, time); const Frame = getFrame(monId, time);
if ( Frame ) { if (Frame) {
const e = events[Frame.EventId];
// Adjust for bulk frames // Adjust for bulk frames
if ( Frame.NextFrameId ) { if (Frame.NextFrameId) {
var e = events[Frame.EventId]; const NextFrame = e.FramesById[Frame.NextFrameId];
var NextFrame = e.FramesById[Frame.NextFrameId]; if (!NextFrame) {
if ( !NextFrame ) {
console.log("No next frame for " + Frame.NextFrameId); console.log("No next frame for " + Frame.NextFrameId);
} else if ( NextFrame.Type == 'Bulk' ) { } else if (NextFrame.Type == 'Bulk') {
// There is time between this frame and a bulk frame // There is time between this frame and a bulk frame
var duration = Frame.NextTimeStampSecs - Frame.TimeStampSecs; const duration = Frame.NextTimeStampSecs - Frame.TimeStampSecs;
frame_id = Frame.FrameId + parseInt( (NextFrame.FrameId-Frame.FrameId) * ( time-Frame.TimeStampSecs )/duration ); frame_id = Frame.FrameId + parseInt( (NextFrame.FrameId-Frame.FrameId) * ( time-Frame.TimeStampSecs )/duration );
//console.log("Have NextFrame: duration: " + duration + " frame_id = " + frame_id + " from " + NextFrame.FrameId + ' - ' + Frame.FrameId + " time: " + (time-Frame.TimeStampSecs) ); //console.log("Have NextFrame: duration: " + duration + " frame_id = " + frame_id + " from " + NextFrame.FrameId + ' - ' + Frame.FrameId + " time: " + (time-Frame.TimeStampSecs) );
} else { } else {
@ -255,24 +252,18 @@ function getImageSource(monId, time) {
} else { } else {
frame_id = Frame.FrameId; frame_id = Frame.FrameId;
} }
Event = events[Frame.EventId];
var storage = Storage[Event.StorageId]; // Storage[0] is guaranteed to exist as we make sure it is there in montagereview.js.php
if ( !storage ) { const storage = Storage[e.StorageId] ? Storage[e.StorageId] : Storage[0];
// Storage[0] is guaranteed to exist as we make sure it is there in montagereview.js.php
console.log("No storage area for id " + Event.StorageId);
storage = Storage[0];
}
// monitorServerId may be 0, which gives us the default Server entry // monitorServerId may be 0, which gives us the default Server entry
var server = storage.ServerId ? Servers[storage.ServerId] : Servers[monitorServerId[monId]]; const server = storage.ServerId ? Servers[storage.ServerId] : Servers[monitorServerId[monId]];
return server.PathToIndex + return server.PathToIndex +
'?view=image&eid=' + Frame.EventId + '&fid='+frame_id + '?view=image&eid=' + Frame.EventId + '&fid='+frame_id +
"&width=" + monitorCanvasObj[monId].width + "&width=" + monitorCanvasObj[monId].width +
"&height=" + monitorCanvasObj[monId].height; "&height=" + monitorCanvasObj[monId].height;
} // end found Frame } // end found Frame
return ''; return '';
//return "no data"; } // end function getImageSource
}
// callback when loading an image. Will load itself to the canvas, or draw no data // callback when loading an image. Will load itself to the canvas, or draw no data
function imagedone( obj, monId, success ) { function imagedone( obj, monId, success ) {
@ -536,7 +527,7 @@ function drawGraph() {
var x1=parseInt( (Frame.TimeStampSecs - minTimeSecs) / rangeTimeSecs * cWidth); // round low end down var x1=parseInt( (Frame.TimeStampSecs - minTimeSecs) / rangeTimeSecs * cWidth); // round low end down
var x2=parseInt( (Frame.TimeStampSecs - minTimeSecs) / rangeTimeSecs * cWidth + 0.5 ); // round up var x2=parseInt( (Frame.TimeStampSecs - minTimeSecs) / rangeTimeSecs * cWidth + 0.5 ); // round up
if (x2-x1 < 2) x2=x1+2; // So it is visible make them all at least this number of seconds wide if (x2-x1 < 2) x2=x1+2; // So it is visible make them all at least this number of seconds wide
ctx.fillStyle=monitorColour[Event.MonitorId]; //ctx.fillStyle=monitorColour[Event.MonitorId];
ctx.globalAlpha = 0.4 + 0.6 * (1 - Frame.Score/maxScore); // Background is scaled but even lowest is twice as dark as the background ctx.globalAlpha = 0.4 + 0.6 * (1 - Frame.Score/maxScore); // Background is scaled but even lowest is twice as dark as the background
ctx.fillRect(x1, monitorIndex[Event.MonitorId]*rowHeight, x2-x1, rowHeight); ctx.fillRect(x1, monitorIndex[Event.MonitorId]*rowHeight, x2-x1, rowHeight);
} // end foreach frame } // end foreach frame
@ -603,6 +594,7 @@ function redrawScreen() {
scaleDiv.hide(); scaleDiv.hide();
fit.text('Scale'); fit.text('Scale');
monitors.height(mh.toString() + 'px'); // leave a small gap at bottom monitors.height(mh.toString() + 'px'); // leave a small gap at bottom
if (maxfit2(monitors.outerWidth(), monitors.outerHeight()) == 0) { /// if we fail to fix we back out of fit mode -- ??? This may need some better handling if (maxfit2(monitors.outerWidth(), monitors.outerHeight()) == 0) { /// if we fail to fix we back out of fit mode -- ??? This may need some better handling
console.log("Failed to fit, dropping back to scaled mode"); console.log("Failed to fit, dropping back to scaled mode");
fitMode=1-fitMode; fitMode=1-fitMode;
@ -610,17 +602,17 @@ function redrawScreen() {
} else { } else {
// switch out of fit mode // switch out of fit mode
// if we fit, then monitors were absolutely positioned already (or will be) otherwise release them to float // if we fit, then monitors were absolutely positioned already (or will be) otherwise release them to float
for ( var i=0; i<numMonitors; i++ ) { for (let i=0; i<numMonitors; i++) {
monitorCanvasObj[monitorPtr[i]].style.position=""; monitorCanvasObj[monitorPtr[i]].style.position = '';
} }
monitors.height('auto'); monitors.height('');
scaleDiv.show(); scaleDiv.show();
fit.text('fit'); fit.text('fit');
setScale(currentScale); setScale(currentScale);
} }
outputUpdate(currentTimeSecs); outputUpdate(currentTimeSecs);
timerFire(); // force a fire in case it's not timing timerFire(); // force a fire in case it's not timing
} } // end function redrawScreen
function outputUpdate(time) { function outputUpdate(time) {
drawSliderOnGraph(time); drawSliderOnGraph(time);
@ -864,7 +856,7 @@ function click_all_events() {
function allnon() { function allnon() {
clicknav(0, 0, 0); clicknav(0, 0, 0);
} }
/// >>>>>>>>>>>>>>>>> handles packing different size/aspect monitors on screen <<<<<<<<<<<<<<<<<<<<<<<< /// handles packing different size/aspect monitors on screen
function compSize(a, b) { // sort array by some size parameter - height seems to work best. A semi-greedy algorithm function compSize(a, b) { // sort array by some size parameter - height seems to work best. A semi-greedy algorithm
var a_value = monitorHeight[a] * monitorWidth[a] * monitorNormalizeScale[a] * monitorZoomScale[a] * monitorNormalizeScale[a] * monitorZoomScale[a]; var a_value = monitorHeight[a] * monitorWidth[a] * monitorNormalizeScale[a] * monitorZoomScale[a] * monitorNormalizeScale[a] * monitorZoomScale[a];
@ -1086,18 +1078,8 @@ function changeDateTime(e) {
// >>>>>>>>> Initialization that runs on window load by being at the bottom // >>>>>>>>> Initialization that runs on window load by being at the bottom
function initPage() { function initPage() {
jQuery(document).ready(function() { if (!liveMode) {
jQuery("#hdrbutton").click(function() { load_Frames(events);
jQuery("#flipMontageHeader").slideToggle("slow");
jQuery("#hdrbutton").toggleClass('glyphicon-menu-down').toggleClass('glyphicon-menu-up');
});
});
for (const event_id in events) {
load_Frames(events[event_id]);
}
if ( !liveMode ) {
canvas = document.getElementById('timeline'); canvas = document.getElementById('timeline');
canvas.addEventListener('mousemove', mmove, false); canvas.addEventListener('mousemove', mmove, false);
@ -1106,7 +1088,7 @@ function initPage() {
canvas.addEventListener('mouseup', mup, false); canvas.addEventListener('mouseup', mup, false);
canvas.addEventListener('mouseout', mout, false); canvas.addEventListener('mouseout', mout, false);
ctx = canvas.getContext('2d'); ctx = canvas.getContext('2d', { willReadFrequently: true });
drawGraph(); drawGraph();
} }
@ -1184,10 +1166,10 @@ function initPage() {
el = $j(this); el = $j(this);
//el.on('change', changeDateTime()); //el.on('change', changeDateTime());
if (el.hasClass('datetimepicker')) { if (el.hasClass('datetimepicker')) {
el.datetimepicker({timeFormat: "HH:mm:ss", dateFormat: "yy-mm-dd", maxDate: 0, constrainInput: false}) el.datetimepicker({timeFormat: "HH:mm:ss", dateFormat: "yy-mm-dd", maxDate: 0, constrainInput: false});
} }
if (el.hasClass('datepicker')) { if (el.hasClass('datepicker')) {
el.datepicker({dateFormat: "yy-mm-dd", maxDate: 0, constrainInput: false}) el.datepicker({dateFormat: "yy-mm-dd", maxDate: 0, constrainInput: false});
} }
}); });
} }
@ -1204,7 +1186,7 @@ function takeSnapshot() {
server = new Server(Servers[serverId]); server = new Server(Servers[serverId]);
$j.ajax({ $j.ajax({
method: 'POST', method: 'POST',
url: server.UrlToApi()+'/snapshots.json' + (auth_relay ? '?' + auth_relay : ''), url: server.urlToApi()+'/snapshots.json' + (auth_relay ? '?' + auth_relay : ''),
data: { 'monitor_ids[]': monitorIndex.keys()}, data: { 'monitor_ids[]': monitorIndex.keys()},
success: function(response) { success: function(response) {
console.log(response); console.log(response);
@ -1219,46 +1201,61 @@ window.addEventListener("resize", redrawScreen, {passive: true});
// Kick everything off // Kick everything off
window.addEventListener('DOMContentLoaded', initPage); window.addEventListener('DOMContentLoaded', initPage);
function load_Frames(zm_event) { function load_Frames(zm_events) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
$j.getJSON(Servers[serverId].UrlToApi()+'/frames/index/EventId:'+zm_event.Id+'.json?'+auth_relay) let url = Servers[serverId].urlToApi()+'/frames/index';
.done(function(data) {
if (data.frames.length) {
/*
const zm_event = events[data.frames[0].Frame.EventId];
if (!zm_event) {
console.error("No event object found for " + data.frames[0].Frame.EventId);
reject(Error("There was an error"));
return;
}
*/
zm_event.FramesById = [];
let last_frame = null;
for (let i=0, len=data.frames.length; i<len; i++) { let query = '';
const frame = data.frames[i].Frame; let ids = Object.keys(zm_events);
// new Date uses browser TZ unless specified in string, so append the server offset
date = new Date(frame.TimeStamp+(server_utc_offset/3600)); while (ids.length) {
frame.TimeStampSecs = new Date(date.getTime() + frame.Delta * 1000).getTime() / 1000; const event_id = ids.shift();
//console.log(date, frame.TimeStamp, frame.Delta, frame.TimeStampSecs); const zm_event = zm_events[event_id];
if (last_frame) {
frame.PrevFrameId = last_frame.Id; query += '/EventId:'+zm_event.Id;
last_frame.NextFrameId = frame.Id; if (!ids.length || (query.length > 1000)) {
last_frame.NextTimeStampSecs = frame.TimeStampSecs; $j.ajax(url+query+'.json?'+auth_relay,
{
timeout: 0,
success: function(data) {
if (data.frames.length) {
zm_event.FramesById = [];
let last_frame = null;
for (let i=0, len=data.frames.length; i<len; i++) {
const frame = data.frames[i].Frame;
const zm_event = events[frame.EventId];
if (!zm_event) {
console.error("No event object found for " + data.frames[0].Frame.EventId);
continue;
}
// new Date uses browser TZ unless specified in string, so append the server offset
date = new Date(frame.TimeStamp+(server_utc_offset/3600));
frame.TimeStampSecs = new Date(date.getTime() + frame.Delta * 1000).getTime() / 1000;
//console.log(date, frame.TimeStamp, frame.Delta, frame.TimeStampSecs);
if (last_frame) {
frame.PrevFrameId = last_frame.Id;
last_frame.NextFrameId = frame.Id;
last_frame.NextTimeStampSecs = frame.TimeStampSecs;
}
last_frame = frame;
if (!zm_event.FramesById) zm_event.FramesById = [];
zm_event.FramesById[frame.Id] = frame;
} // end fireach frame
} // end if there are frames
drawGraph();
resolve();
},
error: function() {
logAjaxFail;
reject(Error("There was an error"));
} }
last_frame = frame; }
); // end ajax
zm_event.FramesById[frame.Id] = frame; query = '';
} // end fireach frame } // end if query string is too long
} // end if there are frames } // end while zm_events.legtnh
drawGraph();
resolve();
})
.fail(function() {
logAjaxFail;
reject(Error("There was an error"));
}
);
} // end Promise } // end Promise
); );
} // end function load_Frames(Event) } // end function load_Frames(Event)

View File

@ -22,7 +22,6 @@ global $minTime;
global $maxTime; global $maxTime;
global $monitors; global $monitors;
global $eventsSql; global $eventsSql;
global $framesSql;
?> ?>
var currentScale=<?php echo $defaultScale?>; var currentScale=<?php echo $defaultScale?>;
@ -64,31 +63,8 @@ if (!$liveMode) {
$EventsById = array(); $EventsById = array();
while ( $event = $result->fetch(PDO::FETCH_ASSOC) ) { while ( $event = $result->fetch(PDO::FETCH_ASSOC) ) {
$event_id = $event['Id']; $EventsById[$event['Id']] = $event;
$EventsById[$event_id] = $event;
} }
$next_frames = array();
if ( 0 ) {
if ( $result = dbQuery($framesSql) ) {
$next_frame = null;
while ( $frame = $result->fetch(PDO::FETCH_ASSOC) ) {
$event_id = $frame['EventId'];
$event = &$EventsById[$event_id];
$frame['TimeStampSecs'] = $event['StartTimeSecs'] + $frame['Delta'];
if ( !isset($event['FramesById']) ) {
// Please note that this is the last frame as we sort DESC
$event['FramesById'] = array();
$frame['NextTimeStampSecs'] = $event['EndTimeSecs'];
} else {
$frame['NextTimeStampSecs'] = $next_frames[$frame['EventId']]['TimeStampSecs'];
$frame['NextFrameId'] = $next_frames[$frame['EventId']]['Id'];
}
$event['FramesById'] += array($frame['Id']=>$frame);
$next_frames[$frame['EventId']] = &$event['FramesById'][$frame['Id']];
}
} // end if dbQuery
}
$events_by_monitor_id = array(); $events_by_monitor_id = array();
@ -111,8 +87,8 @@ if ( 0 ) {
$maxScore = $event['MaxScore']; $maxScore = $event['MaxScore'];
$anyAlarms = true; $anyAlarms = true;
} }
if ( !isset($events_by_monitor_id[$event['MonitorId']]) ) if (!isset($events_by_monitor_id[$event['MonitorId']]))
$events_by_monitor_id[$event['MonitorId']] = array(); $events_by_monitor_id[$event['MonitorId']] = array();
array_push($events_by_monitor_id[$event['MonitorId']], $event_id); array_push($events_by_monitor_id[$event['MonitorId']], $event_id);
} # end foreach Event } # end foreach Event
echo ' }; echo ' };

View File

@ -24,10 +24,9 @@ if ( !canView('System') ) {
} }
xhtmlHeaders(__FILE__, translate('SystemLog')); xhtmlHeaders(__FILE__, translate('SystemLog'));
?> getBodyTopHTML();
<body> echo getNavBarHTML() ?>
<?php echo getNavBarHTML() ?> <div id="content" class="px-3 table-responsive-sm">
<div id="page" class="px-3 table-responsive-sm">
<div id="logSummary" class="text-center"> <div id="logSummary" class="text-center">
<?php echo translate('State') ?>:&nbsp;<span id="logState"></span>&nbsp;-&nbsp; <?php echo translate('State') ?>:&nbsp;<span id="logState"></span>&nbsp;-&nbsp;
@ -36,7 +35,7 @@ xhtmlHeaders(__FILE__, translate('SystemLog'));
<?php echo translate('Displaying') ?>:&nbsp;<span id="displayLogs"></span>&nbsp;-&nbsp; <?php echo translate('Displaying') ?>:&nbsp;<span id="displayLogs"></span>&nbsp;-&nbsp;
<?php echo translate('Updated') ?>:&nbsp;<span id="lastUpdate"></span> <?php echo translate('Updated') ?>:&nbsp;<span id="lastUpdate"></span>
</div> </div>
<div id="logsTable">
<div id="toolbar"> <div id="toolbar">
<button id="backBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Back') ?>" disabled><i class="fa fa-arrow-left"></i></button> <button id="backBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Back') ?>" disabled><i class="fa fa-arrow-left"></i></button>
<button id="refreshBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Refresh') ?>" ><i class="fa fa-refresh"></i></button> <button id="refreshBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Refresh') ?>" ><i class="fa fa-refresh"></i></button>
@ -124,5 +123,6 @@ echo htmlSelect('filterLevel', $levels,
</tbody> </tbody>
</table> </table>
</div><!--page--> </div><!--logstable-->
</div><!--content-->
<?php xhtmlFooter() ?> <?php xhtmlFooter() ?>

View File

@ -364,7 +364,7 @@ getBodyTopHTML();
echo getNavBarHTML(); echo getNavBarHTML();
?> ?>
<div id="page" class="container-fluid"> <div id="page" class="container-fluid">
<div class="row flex-nowrap"> <div id="content" class="row flex-nowrap">
<nav> <!-- BEGIN PILL LIST --> <nav> <!-- BEGIN PILL LIST -->
<ul class="nav nav-pills flex-column h-100" id="pills-tab" role="tablist" aria-orientation="vertical"> <ul class="nav nav-pills flex-column h-100" id="pills-tab" role="tablist" aria-orientation="vertical">
<?php <?php
@ -441,7 +441,7 @@ if (canEdit('Monitors')) {
</div> </div>
<!-- BEGIN ITEM LIST --> <!-- BEGIN ITEM LIST -->
<div class="d-flex flex-row container-fluid pr-0"> <div class="d-flex flex-row container-fluid pr-0" id="monitor">
<form name="contentForm" id="contentForm" method="post" action="?view=monitor" autocomplete="off"> <form name="contentForm" id="contentForm" method="post" action="?view=monitor" autocomplete="off">
<input type="hidden" name="tab" value="<?php echo $tab?>"/> <input type="hidden" name="tab" value="<?php echo $tab?>"/>
<input type="hidden" name="mid" value="<?php echo $monitor->Id() ? $monitor->Id() : $mid ?>"/> <input type="hidden" name="mid" value="<?php echo $monitor->Id() ? $monitor->Id() : $mid ?>"/>

View File

@ -18,18 +18,11 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// //
if ( !canEdit( 'Monitors' ) ) if (!canEdit('Monitors')) {
{ $view = 'error';
$view = "error"; return;
return;
}
$sql = "select Id,Name from MonitorPresets";
$presets = array();
$presets[0] = translate('ChoosePreset');
foreach( dbFetchAll( $sql ) as $preset )
{
$presets[$preset['Id']] = htmlentities( $preset['Name'] );
} }
$mid = isset($_REQUEST['mid']) ? validInt($_REQUEST['mid']) : 0;
$focusWindow = true; $focusWindow = true;
@ -38,20 +31,27 @@ xhtmlHeaders(__FILE__, translate('MonitorPreset') );
<body> <body>
<?php echo getNavBarHTML() ?> <?php echo getNavBarHTML() ?>
<div id="page"> <div id="page">
<h2><?php echo translate('MonitorPreset') ?></h2> <h2><?php echo translate('MonitorPreset') ?></h2>
<div id="content"> <div id="content">
<form name="contentForm" id="monitorPresetForm" method="post" action="?"> <form name="contentForm" id="monitorPresetForm" method="post" action="?">
<input type="hidden" name="view" value="none"/> <input type="hidden" name="view" value="monitor"/>
<input type="hidden" name="mid" value="<?php echo validNum($_REQUEST['mid']) ?>"/> <input type="hidden" name="mid" value="<?php echo $mid ?>"/>
<p> <p>
<?php echo translate('MonitorPresetIntro') ?> <?php echo translate('MonitorPresetIntro') ?>
</p> </p>
<p> <p>
<label for="preset"><?php echo translate('Preset') ?></label><?php echo buildSelect( "preset", $presets ); ?> <label for="preset"><?php echo translate('Preset') ?></label>
<?php
$presets = array();
$presets[0] = translate('ChoosePreset');
foreach (dbFetchAll('SELECT Id,Name FROM MonitorPresets ORDER BY Name') as $preset) {
$presets[$preset['Id']] = htmlentities( $preset['Name'] );
}
echo buildSelect('preset', $presets); ?>
</p> </p>
<div id="contentButtons"> <div id="contentButtons">
<input type="submit" name="saveBtn" value="<?php echo translate('Save') ?>" data-on-click-this="submitPreset" disabled="disabled"/> <button type="submit" name="saveBtn" value="preset" disabled="disabled"><?php echo translate('Save') ?></button>
<input type="button" value="<?php echo translate('Cancel') ?>" data-on-click="backWindow"/> <button type="button" data-on-click="backWindow"><?php echo translate('Cancel') ?></button>
</div> </div>
</form> </form>
</div> </div>

View File

@ -159,30 +159,15 @@ $eventsSql = 'SELECT
WHERE 1 > 0 WHERE 1 > 0
'; ';
// select E.Id,E.Name,UNIX_TIMESTAMP(E.StartDateTime) as StartTimeSecs,UNIX_TIMESTAMP(max(DATE_ADD(E.StartDateTime, Interval Delta+0.5 Second))) as CalcEndTimeSecs, E.Length,max(F.FrameId) as Frames,E.MaxScore,E.Cause,E.Notes,E.Archived,E.MonitorId
// from Events as E
// inner join Monitors as M on (E.MonitorId = M.Id)
// inner join Frames F on F.EventId=E.Id
// where not isnull(E.Frames) and not isnull(StartDateTime) ";
// Note that the delta value seems more accurate than the time stamp for some reason.
$framesSql = '
SELECT Id, FrameId, EventId, TimeStamp, UNIX_TIMESTAMP(TimeStamp) AS TimeStampSecs, Score, Delta, Type
FROM Frames
WHERE EventId IN (SELECT E.Id FROM Events AS E WHERE 1>0
';
// This program only calls itself with the time range involved -- it does all monitors (the user can see, in the called group) all the time // This program only calls itself with the time range involved -- it does all monitors (the user can see, in the called group) all the time
$monitor_ids_sql = ''; $monitor_ids_sql = '';
if ( !empty($user['MonitorIds']) ) { if ( !empty($user['MonitorIds']) ) {
$eventsSql .= ' AND E.MonitorId IN ('.$user['MonitorIds'].')'; $eventsSql .= ' AND E.MonitorId IN ('.$user['MonitorIds'].')';
$framesSql .= ' AND E.MonitorId IN ('.$user['MonitorIds'].')';
} }
if ( count($selected_monitor_ids) ) { if ( count($selected_monitor_ids) ) {
$monitor_ids_sql = ' IN (' . implode(',',$selected_monitor_ids).')'; $monitor_ids_sql = ' IN (' . implode(',',$selected_monitor_ids).')';
$eventsSql .= ' AND E.MonitorId '.$monitor_ids_sql; $eventsSql .= ' AND E.MonitorId '.$monitor_ids_sql;
$framesSql .= ' AND E.MonitorId '.$monitor_ids_sql;
} }
if ( isset($_REQUEST['archive_status']) ) { if ( isset($_REQUEST['archive_status']) ) {
$_SESSION['archive_status'] = $_REQUEST['archive_status']; $_SESSION['archive_status'] = $_REQUEST['archive_status'];
@ -190,14 +175,11 @@ if ( isset($_REQUEST['archive_status']) ) {
if ( isset($_SESSION['archive_status']) ) { if ( isset($_SESSION['archive_status']) ) {
if ( $_SESSION['archive_status'] == 'Archived' ) { if ( $_SESSION['archive_status'] == 'Archived' ) {
$eventsSql .= ' AND E.Archived=1'; $eventsSql .= ' AND E.Archived=1';
$framesSql .= ' AND E.Archived=1';
} else if ( $_SESSION['archive_status'] == 'Unarchived' ) { } else if ( $_SESSION['archive_status'] == 'Unarchived' ) {
$eventsSql .= ' AND E.Archived=0'; $eventsSql .= ' AND E.Archived=0';
$framesSql .= ' AND E.Archived=0';
} }
} }
$fitMode = 1; $fitMode = 1;
if ( isset($_REQUEST['fit']) && ($_REQUEST['fit'] == '0') ) if ( isset($_REQUEST['fit']) && ($_REQUEST['fit'] == '0') )
$fitMode = 0; $fitMode = 0;
@ -222,7 +204,6 @@ for ( $i = 0; $i < count($speeds); $i++ ) {
} }
} }
$liveMode = 1; // default to live $liveMode = 1; // default to live
if ( isset($_REQUEST['live']) && ($_REQUEST['live'] == '0') ) if ( isset($_REQUEST['live']) && ($_REQUEST['live'] == '0') )
$liveMode = 0; $liveMode = 0;
@ -231,8 +212,6 @@ $initialDisplayInterval = 1000;
if ( isset($_REQUEST['displayinterval']) ) if ( isset($_REQUEST['displayinterval']) )
$initialDisplayInterval = validHtmlStr($_REQUEST['displayinterval']); $initialDisplayInterval = validHtmlStr($_REQUEST['displayinterval']);
#$eventsSql .= ' GROUP BY E.Id,E.Name,E.StartDateTime,E.Length,E.Frames,E.MaxScore,E.Cause,E.Notes,E.Archived,E.MonitorId';
$minTimeSecs = $maxTimeSecs = 0; $minTimeSecs = $maxTimeSecs = 0;
if ( isset($minTime) && isset($maxTime) ) { if ( isset($minTime) && isset($maxTime) ) {
if ($minTime >= $maxTime) { if ($minTime >= $maxTime) {
@ -246,16 +225,8 @@ if ( isset($minTime) && isset($maxTime) ) {
$minTimeSecs = strtotime($minTime); $minTimeSecs = strtotime($minTime);
$maxTimeSecs = strtotime($maxTime); $maxTimeSecs = strtotime($maxTime);
$eventsSql .= " AND EndDateTime > '" . $minTime . "' AND StartDateTime < '" . $maxTime . "'"; $eventsSql .= " AND EndDateTime > '" . $minTime . "' AND StartDateTime < '" . $maxTime . "'";
$framesSql .= " AND EndDateTime > '" . $minTime . "' AND StartDateTime < '" . $maxTime . "'";
$framesSql .= ") AND TimeStamp > '" . $minTime . "' AND TimeStamp < '" . $maxTime . "'";
} else {
$framesSql .= ')';
} }
#$framesSql .= ' GROUP BY E.Id, E.MonitorId, F.TimeStamp, F.Delta ORDER BY E.MonitorId, F.TimeStamp ASC';
#$framesSql .= ' GROUP BY E.Id, E.MonitorId, F.TimeStamp, F.Delta ORDER BY E.MonitorId, F.TimeStamp ASC';
$eventsSql .= ' ORDER BY E.Id ASC'; $eventsSql .= ' ORDER BY E.Id ASC';
// DESC is intentional. We process them in reverse order so that we can point each frame to the next one in time.
$framesSql .= ' ORDER BY Id DESC';
$monitors = array(); $monitors = array();
foreach ($displayMonitors as $row) { foreach ($displayMonitors as $row) {
@ -272,11 +243,17 @@ getBodyTopHTML();
?> ?>
<div id="page"> <div id="page">
<?php echo getNavBarHTML() ?> <?php echo getNavBarHTML() ?>
<div id="content">
<form id="montagereview_form" action="?" method="get"> <form id="montagereview_form" action="?" method="get">
<input type="hidden" name="view" value="montagereview"/> <input type="hidden" name="view" value="montagereview"/>
<div id="header">&nbsp;&nbsp; <div id="header">
<a href="#"><span id="hdrbutton" class="glyphicon glyphicon-menu-up pull-right"></span></a> <?php
<div id="flipMontageHeader"> $html = '';
$flip = ( (!isset($_COOKIE['zmMonitorFilterBarFlip'])) or ($_COOKIE['zmMonitorFilterBarFlip'] == 'down')) ? 'up' : 'down';
$html .= '<a class="flip" href="#"><i id="mfbflip" class="material-icons md-18">keyboard_arrow_' .$flip. '</i></a>'.PHP_EOL;
$html .= '<div class="container-fluid" id="mfbpanel"'.( ( $flip == 'down' ) ? ' style="display:none;"' : '' ) .'>'.PHP_EOL;
echo $html;
?>
<?php echo $filter_bar ?> <?php echo $filter_bar ?>
<div id="DateTimeDiv"> <div id="DateTimeDiv">
<input type="text" name="minTime" id="minTime" value="<?php echo preg_replace('/T/', ' ', $minTime ) ?>"/> to <input type="text" name="minTime" id="minTime" value="<?php echo preg_replace('/T/', ' ', $minTime ) ?>"/> to
@ -351,6 +328,7 @@ getBodyTopHTML();
?> ?>
</div> </div>
<p id="fps">evaluating fps</p> <p id="fps">evaluating fps</p>
</div> </div><!--content-->
</div><!--page-->
<script src="<?php echo cache_bust('skins/classic/js/export.js') ?>"></script> <script src="<?php echo cache_bust('skins/classic/js/export.js') ?>"></script>
<?php xhtmlFooter() ?> <?php xhtmlFooter() ?>

View File

@ -56,10 +56,10 @@ xhtmlHeaders(__FILE__, translate('Options'));
getBodyTopHTML(); getBodyTopHTML();
echo getNavBarHTML(); echo getNavBarHTML();
?> ?>
<div class="container-fluid"> <div class="container-fluid" id="content">
<div class="row flex-nowrap"> <div class="row flex-nowrap h-100">
<nav id="sidebar"> <nav id="sidebar">
<ul class="nav nav-pills flex-column h-100"> <ul class="nav nav-pills flex-column">
<?php <?php
foreach ($tabs as $name=>$value) { foreach ($tabs as $name=>$value) {
?> ?>
@ -70,8 +70,6 @@ foreach ($tabs as $name=>$value) {
</ul> </ul>
</nav> </nav>
<div class="container-fluid col-sm-offset-2 h-100 pr-0"> <div class="container-fluid col-sm-offset-2 h-100 pr-0">
<br/>
<div id="options">
<?php <?php
if ($tab == 'skins') { if ($tab == 'skins') {
?> ?>
@ -332,79 +330,7 @@ foreach (array_map('basename', glob('skins/'.$skin.'/css/*', GLOB_ONLYDIR)) as $
</form> </form>
<?php <?php
} else if ($tab == 'API') { } else if ($tab == 'API') {
$apiEnabled = dbFetchOne('SELECT Value FROM Config WHERE Name=\'ZM_OPT_USE_API\''); include('_options_api.php');
if ($apiEnabled['Value'] != '1') {
echo '<div class="errorText">APIs are disabled. To enable, please turn on OPT_USE_API in Options->System</div>';
} else {
?>
<form name="userForm" method="post" action="?">
<button class="float-left" type="submit" name="updateSelected" id="updateSelected"><?php echo translate('Update')?></button>
<button class="btn-danger float-right" type="submit" name="revokeAllTokens" id="revokeAllTokens"><?php echo translate('RevokeAllTokens')?></button>
<br/>
<?php
function revokeAllTokens() {
$minTokenTime = time();
dbQuery('UPDATE `Users` SET `TokenMinExpiry`=?', array($minTokenTime));
echo '<span class="timedSuccessBox">'.translate('AllTokensRevoked').'</span>';
}
function updateSelected() {
# Turn them all off, then selectively turn the checked ones back on
dbQuery('UPDATE `Users` SET `APIEnabled`=0');
if (isset($_REQUEST['tokenUids'])) {
foreach ($_REQUEST['tokenUids'] as $markUid) {
$minTime = time();
dbQuery('UPDATE `Users` SET `TokenMinExpiry`=? WHERE `Id`=?', array($minTime, $markUid));
}
}
if (isset($_REQUEST['apiUids'])) {
foreach ($_REQUEST['apiUids'] as $markUid) {
dbQuery('UPDATE `Users` SET `APIEnabled`=1 WHERE `Id`=?', array($markUid));
}
}
echo '<span class="timedSuccessBox">'.translate('Updated').'</span>';
}
if (array_key_exists('revokeAllTokens', $_POST)) {
revokeAllTokens();
}
if (array_key_exists('updateSelected', $_POST)) {
updateSelected();
}
?>
<br/><br/>
<input type="hidden" name="view" value="<?php echo $view ?>"/>
<input type="hidden" name="tab" value="<?php echo $tab ?>"/>
<input type="hidden" name="action" value="delete"/>
<table id="contentTable" class="table table-striped">
<thead class="thead-highlight">
<tr>
<th class="colUsername"><?php echo translate('Username') ?></th>
<th class="colMark"><?php echo translate('Revoke Token') ?></th>
<th class="colMark"><?php echo translate('API Enabled') ?></th>
</tr>
</thead>
<tbody>
<?php
$sql = 'SELECT * FROM Users ORDER BY Username';
foreach (dbFetchAll($sql) as $row) {
?>
<tr>
<td class="colUsername"><?php echo validHtmlStr($row['Username']) ?></td>
<td class="colMark"><input type="checkbox" name="tokenUids[]" value="<?php echo $row['Id'] ?>" /></td>
<td class="colMark"><input type="checkbox" name="apiUids[]" value="<?php echo $row['Id']?>" <?php echo $row['APIEnabled']?'checked':''?> /></td>
</tr>
<?php
}
?>
</tbody>
</table>
</form>
<?php
} // API enabled
} // $tab == API } // $tab == API
else { else {
$config = array(); $config = array();
@ -475,6 +401,10 @@ foreach (array_map('basename', glob('skins/'.$skin.'/css/*', GLOB_ONLYDIR)) as $
<input type="hidden" name="view" value="<?php echo $view ?>"/> <input type="hidden" name="view" value="<?php echo $view ?>"/>
<input type="hidden" name="tab" value="<?php echo $tab ?>"/> <input type="hidden" name="tab" value="<?php echo $tab ?>"/>
<input type="hidden" name="action" value="options"/> <input type="hidden" name="action" value="options"/>
<div id="contentButtons">
<button type="submit" <?php echo $canEdit?'':' disabled="disabled"' ?>><?php echo translate('Save') ?></button>
</div>
<div id="options">
<?php <?php
if (!isset($configCats[$tab])) { if (!isset($configCats[$tab])) {
echo 'There are no config entries for category '.$tab.'.<br/>'; echo 'There are no config entries for category '.$tab.'.<br/>';
@ -548,15 +478,12 @@ foreach (array_map('basename', glob('skins/'.$skin.'/css/*', GLOB_ONLYDIR)) as $
} # end foreach config entry in the category } # end foreach config entry in the category
} # end if category exists } # end if category exists
?> ?>
<div id="contentButtons"> </div><!--options-->
<button type="submit" <?php echo $canEdit?'':' disabled="disabled"' ?>><?php echo translate('Save') ?></button>
</div>
</form> </form>
<?php <?php
} }
?> ?>
</div><!-- end #options --> </div><!-- end #options -->
</div>
</div> <!-- end row --> </div> <!-- end row -->
</div> </div>
<?php xhtmlFooter() ?> <?php xhtmlFooter() ?>

View File

@ -24,8 +24,6 @@ include('_monitor_filters.php');
$filterbar = ob_get_contents(); $filterbar = ob_get_contents();
ob_end_clean(); ob_end_clean();
noCacheHeaders();
xhtmlHeaders( __FILE__, translate('Console'));
if ( isset($_REQUEST['minTime']) ) { if ( isset($_REQUEST['minTime']) ) {
$minTime = validHtmlStr($_REQUEST['minTime']); $minTime = validHtmlStr($_REQUEST['minTime']);
@ -105,20 +103,25 @@ while ( $event = $result->fetch(PDO::FETCH_ASSOC) ) {
$EventsByMonitor[$event['MonitorId']]['Events'][] = $Event; $EventsByMonitor[$event['MonitorId']]['Events'][] = $Event;
} # end foreach event } # end foreach event
noCacheHeaders();
xhtmlHeaders( __FILE__, translate('Report Event Audit'));
getBodyTopHTML();
echo $navbar;
?> ?>
<body> <div id="page">
<?php echo $navbar ?> <div id="content">
<form name="monitorForm" method="post" action="?view=<?php echo $view ?>"> <form name="monitorForm" method="post" action="?view=<?php echo $view ?>">
<div class="filterBar"> <div class="filterBar">
<?php echo $filterbar ?> <?php echo $filterbar ?>
<div id="DateTimeDiv"> <div id="DateTimeDiv">
<label><?php echo translate('Event Start Time') ?></label> <label><?php echo translate('Event Start Time') ?></label>
<input type="text" name="minTime" id="minTime" value="<?php echo preg_replace('/T/', ' ', $minTime) ?>"/> <?php echo translate('to') ?> <input type="text" name="minTime" id="minTime" value="<?php echo preg_replace('/T/', ' ', $minTime) ?>"/> <?php echo translate('to') ?>
<input type="text" name="maxTime" id="maxTime" value="<?php echo preg_replace('/T/', ' ', $maxTime) ?>"/> <input type="text" name="maxTime" id="maxTime" value="<?php echo preg_replace('/T/', ' ', $maxTime) ?>"/>
</div> </div>
</div><!--FilterBar--> </div><!--FilterBar-->
</form>
<div class="container-fluid"> <div class="container-fluid" id="results">
<table class="table table-striped table-hover table-condensed" id="consoleTable"> <table class="table table-striped table-hover table-condensed" id="consoleTable">
<thead class="thead-highlight"> <thead class="thead-highlight">
<tr> <tr>
@ -205,5 +208,6 @@ for ( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
</tbody> </tbody>
</table> </table>
</div> </div>
</form> </div>
</div>
<?php xhtmlFooter() ?> <?php xhtmlFooter() ?>

View File

@ -38,10 +38,11 @@ if ( $user['MonitorIds'] ) {
$monitor_ids = explode(',', $user['MonitorIds']); $monitor_ids = explode(',', $user['MonitorIds']);
} }
xhtmlHeaders(__FILE__, translate('Snapshot').' '.$snapshot->Id()); xhtmlHeaders(__FILE__, translate('Snapshot').' '.$snapshot->Id());
getBodyTopHTML();
echo getNavBarHTML();
?> ?>
<body>
<div id="page"> <div id="page">
<?php echo getNavBarHTML() ?> <div id="content">
<?php <?php
if ( !$snapshot->Id() ) { if ( !$snapshot->Id() ) {
echo '<div class="error">Snapshot was not found.</div>'; echo '<div class="error">Snapshot was not found.</div>';
@ -66,7 +67,7 @@ if ( !$snapshot->Id() ) {
<h2><?php echo translate('Snapshot').' '.$snapshot->Id() ?></h2> <h2><?php echo translate('Snapshot').' '.$snapshot->Id() ?></h2>
</div> </div>
<div class="d-flex flex-row justify-content-between py-1"> <div class="d-flex flex-row justify-content-between py-1" id="snapshot">
<!-- <!--
<div class="form-group"><label><?php echo translate('Created By') ?></label> <div class="form-group"><label><?php echo translate('Created By') ?></label>
--> -->
@ -81,9 +82,10 @@ if ( !$snapshot->Id() ) {
<textarea name="snapshot[Description]"><?php echo validHtmlStr($snapshot->Description()); ?></textarea> <textarea name="snapshot[Description]"><?php echo validHtmlStr($snapshot->Description()); ?></textarea>
</div> </div>
</div> </div>
</form>
<?php if ( $snapshot->Id() ) { ?> <?php if ( $snapshot->Id() ) { ?>
<!-- BEGIN VIDEO CONTENT ROW --> <!-- BEGIN VIDEO CONTENT ROW -->
<div id="content" class="justify-content-center"> <div id="video" class="row justify-content-center">
<?php <?php
$events = $snapshot->Events(); $events = $snapshot->Events();
$width = 100 / ( count($events) < 2 ? 1 : ( ( count($events) < 4 ) ? count($events) : 4 ) )-1; $width = 100 / ( count($events) < 2 ? 1 : ( ( count($events) < 4 ) ? count($events) : 4 ) )-1;
@ -94,7 +96,6 @@ if ( !$snapshot->Id() ) {
?> ?>
</div><!--content--> </div><!--content-->
<?php } // end if snapshot->Id() ?> <?php } // end if snapshot->Id() ?>
</form>
<h2 id="downloadProgress" class="<?php <h2 id="downloadProgress" class="<?php
if ( isset($_REQUEST['generated']) ) { if ( isset($_REQUEST['generated']) ) {
if ( $_REQUEST['generated'] ) if ( $_REQUEST['generated'] )
@ -116,5 +117,6 @@ if ( !$snapshot->Id() ) {
?></span> ?></span>
<span id="downloadProgressTicker"></span> <span id="downloadProgressTicker"></span>
</h2> </h2>
</div>
</div><!--page--> </div><!--page-->
<?php xhtmlFooter() ?> <?php xhtmlFooter() ?>

View File

@ -34,62 +34,66 @@ getBodyTopHTML();
?> ?>
<?php echo getNavBarHTML() ?> <?php echo getNavBarHTML() ?>
<div id="page" class="container-fluid p-3"> <div id="page">
<!-- Toolbar button placement and styling handled by bootstrap-tables --> <div id="content">
<div id="toolbar"> <!-- Toolbar button placement and styling handled by bootstrap-tables -->
<button id="backBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Back') ?>" disabled><i class="fa fa-arrow-left"></i></button> <div id="toolbar">
<button id="refreshBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Refresh') ?>" ><i class="fa fa-refresh"></i></button> <button id="backBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Back') ?>" disabled><i class="fa fa-arrow-left"></i></button>
<!--<button id="filterBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Filter') ?>"><i class="fa fa-filter"></i></button>--> <button id="refreshBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Refresh') ?>" ><i class="fa fa-refresh"></i></button>
<!--<button id="exportBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Export') ?>" disabled><i class="fa fa-external-link"></i></button>--> <!--<button id="filterBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Filter') ?>"><i class="fa fa-filter"></i></button>-->
<button id="deleteBtn" class="btn btn-danger" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Delete') ?>" disabled><i class="fa fa-trash"></i></button> <!--<button id="exportBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Export') ?>" disabled><i class="fa fa-external-link"></i></button>-->
</div> <button id="deleteBtn" class="btn btn-danger" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Delete') ?>" disabled><i class="fa fa-trash"></i></button>
</div>
<!-- Table styling handled by bootstrap-tables --> <div id="snapshots" class="container-fluid">
<div class="row justify-content-center table-responsive-sm"> <!-- Table styling handled by bootstrap-tables -->
<table <div class="row justify-content-center table-responsive-sm">
id="snapshotTable" <table
data-locale="<?php echo i18n() ?>" id="snapshotTable"
data-side-pagination="server" data-locale="<?php echo i18n() ?>"
data-ajax="ajaxRequest" data-side-pagination="server"
data-pagination="true" data-ajax="ajaxRequest"
data-show-pagination-switch="true" data-pagination="true"
data-page-list="[10, 25, 50, 100, 200, All]" data-show-pagination-switch="true"
data-search="true" data-page-list="[10, 25, 50, 100, 200, All]"
data-cookie="true" data-search="true"
data-cookie-id-table="zmSnapshotsTable" data-cookie="true"
data-cookie-expire="2y" data-cookie-id-table="zmSnapshotsTable"
data-click-to-select="true" data-cookie-expire="2y"
data-remember-order="true" data-click-to-select="true"
data-show-columns="true" data-remember-order="true"
data-show-export="true" data-show-columns="true"
data-uncheckAll="true" data-show-export="true"
data-toolbar="#toolbar" data-uncheckAll="true"
data-show-fullscreen="true" data-toolbar="#toolbar"
data-click-to-select="true" data-show-fullscreen="true"
data-maintain-meta-data="true" data-click-to-select="true"
data-buttons-class="btn btn-normal" data-maintain-meta-data="true"
data-show-jump-to="true" data-buttons-class="btn btn-normal"
data-show-refresh="true" data-show-jump-to="true"
class="table-sm table-borderless" data-show-refresh="true"
style="display:none;" class="table-sm table-borderless"
> style="display:none;"
<thead> >
<!-- Row styling is handled by bootstrap-tables --> <thead>
<tr> <!-- Row styling is handled by bootstrap-tables -->
<th data-sortable="false" data-field="toggleCheck" data-checkbox="true"></th> <tr>
<th data-sortable="true" data-field="Id"><?php echo translate('Id') ?></th> <th data-sortable="false" data-field="toggleCheck" data-checkbox="true"></th>
<th data-sortable="true" data-field="Name"><?php echo translate('Reference') ?></th> <th data-sortable="true" data-field="Id"><?php echo translate('Id') ?></th>
<th data-sortable="false" data-field="Description"><?php echo translate('Notes') ?></th> <th data-sortable="true" data-field="Name"><?php echo translate('Reference') ?></th>
<th data-sortable="true" data-field="CreatedOn"><?php echo translate('When') ?></th> <th data-sortable="false" data-field="Description"><?php echo translate('Notes') ?></th>
<th data-sortable="false" data-field="Thumbnail"><?php echo translate('Thumbnail') ?></th> <th data-sortable="true" data-field="CreatedOn"><?php echo translate('When') ?></th>
</tr> <th data-sortable="false" data-field="Thumbnail"><?php echo translate('Thumbnail') ?></th>
</thead> </tr>
</thead>
<tbody> <tbody>
<!-- Row data populated via Ajax --> <!-- Row data populated via Ajax -->
</tbody> </tbody>
</table> </table>
</div> </div>
</div>
</div><!--content-->
</div> </div>
<?php xhtmlFooter() ?> <?php xhtmlFooter() ?>

View File

@ -171,9 +171,9 @@ if ($monitor->JanusEnabled()) {
noCacheHeaders(); noCacheHeaders();
xhtmlHeaders(__FILE__, $monitor->Name().' - '.translate('Feed')); xhtmlHeaders(__FILE__, $monitor->Name().' - '.translate('Feed'));
?> getBodyTopHTML();
<body> echo getNavBarHTML() ?>
<?php echo getNavBarHTML() ?> <div id="page">
<div id="header"> <div id="header">
<div class="controlHeader"> <div class="controlHeader">
<form method="get"> <form method="get">
@ -239,6 +239,7 @@ $seconds = translate('seconds');
$minute = translate('minute'); $minute = translate('minute');
$minutes = translate('minutes'); $minutes = translate('minutes');
$cyclePeriodOptions = array( $cyclePeriodOptions = array(
5 => '5 '.$seconds,
10 => '10 '.$seconds, 10 => '10 '.$seconds,
30 => '30 '.$seconds, 30 => '30 '.$seconds,
60 => '1 '.$minute, 60 => '1 '.$minute,