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

This commit is contained in:
Isaac Connor 2020-08-20 17:12:41 -04:00
commit 519bfae1da
37 changed files with 3353 additions and 438 deletions

View File

@ -41,7 +41,6 @@ env:
- SMPFLAGS=-j4 OS=ubuntu DIST=xenial DOCKER_REPO=iconzm/packpack - SMPFLAGS=-j4 OS=ubuntu DIST=xenial DOCKER_REPO=iconzm/packpack
- SMPFLAGS=-j4 OS=ubuntu DIST=bionic DOCKER_REPO=iconzm/packpack - SMPFLAGS=-j4 OS=ubuntu DIST=bionic DOCKER_REPO=iconzm/packpack
- SMPFLAGS=-j4 OS=ubuntu DIST=disco DOCKER_REPO=iconzm/packpack - SMPFLAGS=-j4 OS=ubuntu DIST=disco DOCKER_REPO=iconzm/packpack
- SMPFLAGS=-j4 OS=ubuntu DIST=eoan DOCKER_REPO=knnniggett/packpack:ubuntu-eoan
- SMPFLAGS=-j4 OS=ubuntu DIST=focal DOCKER_REPO=iconzm/packpack - SMPFLAGS=-j4 OS=ubuntu DIST=focal DOCKER_REPO=iconzm/packpack
- SMPFLAGS=-j4 OS=debian DIST=jessie DOCKER_REPO=iconzm/packpack - SMPFLAGS=-j4 OS=debian DIST=jessie DOCKER_REPO=iconzm/packpack
- SMPFLAGS=-j4 OS=debian DIST=stretch DOCKER_REPO=iconzm/packpack - SMPFLAGS=-j4 OS=debian DIST=stretch DOCKER_REPO=iconzm/packpack

View File

@ -484,7 +484,7 @@ CREATE TABLE `Monitors` (
`Hue` mediumint(7) NOT NULL default '-1', `Hue` mediumint(7) NOT NULL default '-1',
`Colour` mediumint(7) NOT NULL default '-1', `Colour` mediumint(7) NOT NULL default '-1',
`EventPrefix` varchar(32) NOT NULL default 'Event-', `EventPrefix` varchar(32) NOT NULL default 'Event-',
`LabelFormat` varchar(64) default '%N - %y/%m/%d %H:%M:%S', `LabelFormat` varchar(64),
`LabelX` smallint(5) unsigned NOT NULL default '0', `LabelX` smallint(5) unsigned NOT NULL default '0',
`LabelY` smallint(5) unsigned NOT NULL default '0', `LabelY` smallint(5) unsigned NOT NULL default '0',
`LabelSize` smallint(5) unsigned NOT NULL DEFAULT '1', `LabelSize` smallint(5) unsigned NOT NULL DEFAULT '1',

2
db/zm_update-1.34.20.sql Normal file
View File

@ -0,0 +1,2 @@
/* This was done in 1.31.0 but zm_create.sql.in wasn't updated to match. */
ALTER TABLE Monitors MODIFY LinkedMonitors varchar(255);

View File

@ -32,10 +32,14 @@ Version: 1.35.5
Release: 1%{?dist} Release: 1%{?dist}
Summary: A camera monitoring and analysis tool Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons Group: System Environment/Daemons
# Mootools is inder the MIT license: http://mootools.net/ # Mootools is under the MIT license: http://mootools.net/
# jQuery is under the MIT license: https://jquery.org/license/
# CakePHP is under the MIT license: https://github.com/cakephp/cakephp # CakePHP is under the MIT license: https://github.com/cakephp/cakephp
# Crud is under the MIT license: https://github.com/FriendsOfCake/crud # Crud is under the MIT license: https://github.com/FriendsOfCake/crud
# CakePHP-Enum-Behavior is under the MIT license: https://github.com/asper/CakePHP-Enum-Behavior # CakePHP-Enum-Behavior is under the MIT license: https://github.com/asper/CakePHP-Enum-Behavior
# Bootstrap is under the MIT license: https://getbootstrap.com/docs/4.5/about/license/
# Bootstrap-table is under the MIT license: https://bootstrap-table.com/docs/about/license/
# font-awesome is under CC-BY license: https://fontawesome.com/license/free
License: GPLv2+ and LGPLv2+ and MIT License: GPLv2+ and LGPLv2+ and MIT
URL: http://www.zoneminder.com/ URL: http://www.zoneminder.com/

View File

@ -23,7 +23,7 @@ override_dh_auto_configure:
-DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \ -DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \
-DZM_RUNDIR="/run/zm" \ -DZM_RUNDIR="/run/zm" \
-DZM_SOCKDIR="/run/zm" \ -DZM_SOCKDIR="/run/zm" \
-DZM_TMPDIR="/tmp/zm" \ -DZM_TMPDIR="/var/tmp/zm" \
-DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \ -DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \
-DZM_CACHEDIR="/var/cache/zoneminder/cache" \ -DZM_CACHEDIR="/var/cache/zoneminder/cache" \
-DZM_DIR_EVENTS="/var/cache/zoneminder/events" \ -DZM_DIR_EVENTS="/var/cache/zoneminder/events" \

View File

@ -61,10 +61,10 @@ if [ "$1" = "configure" ]; then
exit 1; exit 1;
fi fi
# This creates the user. # This creates the user.
echo "CREATE USER '${ZM_DB_USER}'@localhost IDENTIFIED BY '${ZM_DB_PASS}';" | mysql --defaults-file=/etc/mysql/debian.cnf mysql echo "CREATE USER '${ZM_DB_USER}'@${ZM_DB_HOST} IDENTIFIED BY '${ZM_DB_PASS}';" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
fi fi
echo "Updating permissions" echo "Updating permissions"
echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql echo "GRANT LOCK tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine,trigger,execute ON ${ZM_DB_NAME}.* TO '${ZM_DB_USER}'@${ZM_DB_HOST};" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
zmupdate.pl --nointeractive zmupdate.pl --nointeractive
zmupdate.pl --nointeractive -f zmupdate.pl --nointeractive -f

View File

@ -842,7 +842,8 @@ bool EventStream::sendFrame(int delta_us) {
(0 > fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size) ) (0 > fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size) )
|| ||
( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) ) { ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) ) {
Error("Unable to send stream frame: %s", strerror(errno)); if ( errno != EPIPE )
Error("Unable to send stream frame: %s", strerror(errno));
return false; return false;
} }
} // end if send_raw or not } // end if send_raw or not

View File

@ -81,7 +81,7 @@ void FFMPEGInit() {
av_log_set_callback(log_libav_callback); av_log_set_callback(log_libav_callback);
Info("Enabling ffmpeg logs, as LOG_DEBUG+LOG_FFMPEG are enabled in options"); Info("Enabling ffmpeg logs, as LOG_DEBUG+LOG_FFMPEG are enabled in options");
} else { } else {
Info("Not enabling ffmpeg logs, as LOG_FFMPEG and/or LOG_DEBUG is disabled in options, or this monitor is not part of your debug targets"); Debug(1,"Not enabling ffmpeg logs, as LOG_FFMPEG and/or LOG_DEBUG is disabled in options, or this monitor is not part of your debug targets");
av_log_set_level(AV_LOG_QUIET); av_log_set_level(AV_LOG_QUIET);
} }
#if !LIBAVFORMAT_VERSION_CHECK(58, 9, 0, 64, 0) #if !LIBAVFORMAT_VERSION_CHECK(58, 9, 0, 64, 0)

View File

@ -40,6 +40,7 @@ extern "C" {
#include <libavutil/mathematics.h> #include <libavutil/mathematics.h>
#include <libavutil/avstring.h> #include <libavutil/avstring.h>
#include "libavutil/audio_fifo.h" #include "libavutil/audio_fifo.h"
#include "libavutil/imgutils.h"
/* LIBAVUTIL_VERSION_CHECK checks for the right version of libav and FFmpeg /* LIBAVUTIL_VERSION_CHECK checks for the right version of libav and FFmpeg
* The original source is vlc (in modules/codec/avcodec/avcommon_compat.h) * The original source is vlc (in modules/codec/avcodec/avcommon_compat.h)

View File

@ -14,6 +14,18 @@ class UsersController extends AppController {
*/ */
public $components = array('RequestHandler', 'Paginator'); public $components = array('RequestHandler', 'Paginator');
public function beforeFilter() {
parent::beforeFilter();
global $user;
# We already tested for auth in appController, so we just need to test for specific permission
$canView = (!$user) || ($user['System'] != 'None');
if ( !$canView ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
}
/** /**
* index method * index method
* *
@ -23,6 +35,12 @@ class UsersController extends AppController {
public function index() { public function index() {
$this->User->recursive = 0; $this->User->recursive = 0;
global $user;
# We should actually be able to list our own user, but I'm not bothering at this time.
if ( $user['System'] == 'None' ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
$users = $this->Paginator->paginate('User'); $users = $this->Paginator->paginate('User');
$this->set(compact('users')); $this->set(compact('users'));
@ -37,9 +55,19 @@ class UsersController extends AppController {
*/ */
public function view($id = null) { public function view($id = null) {
$this->User->recursive = 1; $this->User->recursive = 1;
if (!$this->User->exists($id)) {
global $user;
# We can view ourselves
$canView = ($user['System'] != 'None') or ($user['Id'] == $id);
if ( !$canView ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
if ( !$this->User->exists($id) ) {
throw new NotFoundException(__('Invalid user')); throw new NotFoundException(__('Invalid user'));
} }
$options = array('conditions' => array('User.' . $this->User->primaryKey => $id)); $options = array('conditions' => array('User.' . $this->User->primaryKey => $id));
$user = $this->User->find('first', $options); $user = $this->User->find('first', $options);
@ -55,9 +83,16 @@ class UsersController extends AppController {
* @return void * @return void
*/ */
public function add() { public function add() {
if ($this->request->is('post')) { if ( $this->request->is('post') ) {
global $user;
if ( $user['System'] != 'Edit' ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
$this->User->create(); $this->User->create();
if ($this->User->save($this->request->data)) { if ( $this->User->save($this->request->data) ) {
return $this->flash(__('The user has been saved.'), array('action' => 'index')); return $this->flash(__('The user has been saved.'), array('action' => 'index'));
} }
$this->Session->setFlash( $this->Session->setFlash(
@ -76,7 +111,14 @@ class UsersController extends AppController {
public function edit($id = null) { public function edit($id = null) {
$this->User->id = $id; $this->User->id = $id;
if (!$this->User->exists($id)) { global $user;
$canEdit = ($user['System'] == 'Edit') or (($user['Id'] == $id) and ZM_USER_SELF_EDIT);
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
if ( !$this->User->exists($id) ) {
throw new NotFoundException(__('Invalid user')); throw new NotFoundException(__('Invalid user'));
} }
@ -87,8 +129,10 @@ class UsersController extends AppController {
$message = 'Error'; $message = 'Error';
} }
} else { } else {
# What is this doing? Resetting the request data? I understand clearing the password field
# but generally I feel like the request data should be read only
$this->request->data = $this->User->read(null, $id); $this->request->data = $this->User->read(null, $id);
unset($this->request->data['User']['password']); unset($this->request->data['User']['Password']);
} }
$this->set(array( $this->set(array(
@ -106,6 +150,13 @@ class UsersController extends AppController {
*/ */
public function delete($id = null) { public function delete($id = null) {
$this->User->id = $id; $this->User->id = $id;
global $user;
# Can't delete ourselves
if ( ($user['System'] != 'Edit') or ($user['Id'] == $id) ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
if ( !$this->User->exists() ) { if ( !$this->User->exists() ) {
throw new NotFoundException(__('Invalid user')); throw new NotFoundException(__('Invalid user'));
} }
@ -120,35 +171,4 @@ class UsersController extends AppController {
'_serialize' => array('message') '_serialize' => array('message')
)); ));
} }
} # end class UsersController
public function beforeFilter() {
parent::beforeFilter();
if ( ZM_OPT_USE_AUTH ) {
$this->Auth->allow('add', 'logout');
} else {
$this->Auth->allow();
}
}
public function login() {
if ( !ZM_OPT_USE_AUTH ) {
$this->set(array(
'message' => 'Login is not required.',
'_serialize' => array('message')
));
return;
}
if ( $this->request->is('post') ) {
if ( $this->Auth->login() ) {
return $this->redirect($this->Auth->redirectUrl());
}
$this->Session->setFlash(__('Invalid username or password, try again'));
}
}
public function logout() {
return $this->redirect($this->Auth->logout());
}
}

View File

@ -21,6 +21,20 @@ class User extends AppModel {
) )
); );
function beforeFind($query) {
if ( empty($query['fields']) ) {
$schema = $this->schema();
unset($schema['Password']);
foreach (array_keys($schema) as $field) {
$query['fields'][] = $this->alias . '.' . $field;
}
return $query;
}
return parent::beforeFind($query);
}
/** /**
* Use table * Use table
* *

10
web/css/bootstrap-table.min.css vendored Normal file

File diff suppressed because one or more lines are too long

4
web/css/font-awesome.min.css vendored Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -106,7 +106,7 @@ if ( ! is_dir("skins/$skin") ) {
$skin = $skins[0]; $skin = $skins[0];
} }
} }
global $css;
if ( isset($_GET['css']) ) { if ( isset($_GET['css']) ) {
$css = $_GET['css']; $css = $_GET['css'];
} else if ( isset($_COOKIE['zmCSS']) ) { } else if ( isset($_COOKIE['zmCSS']) ) {

View File

@ -576,6 +576,13 @@ input[type=submit],
text-align: center; text-align: center;
} }
.btn-normal,
.btn-normal:link {
color: #fff;
background-color: #3498db;
border: 1px solid #3498db;
}
button:hover, button:hover,
input[type=button]:hover, input[type=button]:hover,
input[type=submit]:hover, input[type=submit]:hover,
@ -701,6 +708,10 @@ li.search-choice {
.gi-4x{font-size: 4em;} .gi-4x{font-size: 4em;}
.gi-5x{font-size: 5em;} .gi-5x{font-size: 5em;}
.fa:hover{
color: black;
}
.filterBar { .filterBar {
margin-top:5px; margin-top:5px;
margin-bottom:5px; margin-bottom:5px;
@ -713,7 +724,11 @@ li.search-choice {
#dropdown_storage+div, #dropdown_storage+div,
#dropdown_reminder+div, #dropdown_reminder+div,
#dropdown_bandwidth+div { #dropdown_bandwidth+div {
background-color:#485460; background-color:#485460;
}
#framesTable td:hover {
cursor: pointer;
} }
.zoom { .zoom {
@ -723,6 +738,7 @@ background-color:#485460;
} }
.zoom:hover { .zoom:hover {
transform: scale(5); /* (150% zoom - Note: if the zoom is too large, it will go outside of the viewport) */ transform-origin: 70% 50%;
transform: scale(5); /* (arbitray zoom value - Note if the zoom is too large, it will go outside of the viewport) */
} }

View File

@ -1,5 +1,5 @@
.archived { .archived {
background-color: #f8f8f8;; background-color: #ffc107;;
} }
#contentTable.major .colTime { #contentTable.major .colTime {

View File

@ -27,6 +27,7 @@ function exportHeader($title) {
<title><?php echo $title ?></title> <title><?php echo $title ?></title>
<style> <style>
<?php <?php
global $css;
include(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/css/base/export.css'); include(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/css/base/export.css');
if ( $css != 'base' ) { if ( $css != 'base' ) {
include(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/css/'.$css.'/export.css'); include(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/css/'.$css.'/export.css');

View File

@ -85,7 +85,9 @@ if ( file_exists("skins/$skin/css/$css/graphics/favicon.ico") ) {
echo output_cache_busted_stylesheet_links(array( echo output_cache_busted_stylesheet_links(array(
'css/reset.css', 'css/reset.css',
'css/overlay.css', 'css/overlay.css',
'css/font-awesome.min.css',
'css/bootstrap.min.css', 'css/bootstrap.min.css',
'css/bootstrap-table.min.css',
)); ));
echo output_link_if_exists(array( echo output_link_if_exists(array(
@ -130,6 +132,8 @@ if ( $css != 'base' )
<script src="skins/<?php echo $skin; ?>/js/jquery.js"></script> <script src="skins/<?php echo $skin; ?>/js/jquery.js"></script>
<script src="skins/<?php echo $skin; ?>/js/jquery-ui-1.12.1/jquery-ui.js"></script> <script src="skins/<?php echo $skin; ?>/js/jquery-ui-1.12.1/jquery-ui.js"></script>
<script src="skins/<?php echo $skin; ?>/js/bootstrap.min.js"></script> <script src="skins/<?php echo $skin; ?>/js/bootstrap.min.js"></script>
<script src="skins/<?php echo $skin; ?>/js/bootstrap-table.min.js"></script>
<script src="skins/<?php echo $skin; ?>/js/bootstrap-table-cookie.min.js"></script>
<script src="skins/<?php echo $skin; ?>/js/chosen/chosen.jquery.min.js"></script> <script src="skins/<?php echo $skin; ?>/js/chosen/chosen.jquery.min.js"></script>
<script src="skins/<?php echo $skin; ?>/js/dateTimePicker/jquery-ui-timepicker-addon.js"></script> <script src="skins/<?php echo $skin; ?>/js/dateTimePicker/jquery-ui-timepicker-addon.js"></script>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -646,6 +646,17 @@ function setCookie(name, value, days) {
document.cookie = name + "=" + (value || "") + expires + "; path=/"; document.cookie = name + "=" + (value || "") + expires + "; path=/";
} }
function getCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for (var i=0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
}
return null;
}
function delCookie(name) { function delCookie(name) {
document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;'; document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;';
} }

View File

@ -106,51 +106,45 @@ xhtmlHeaders(__FILE__, translate('Events') );
<body> <body>
<div id="page"> <div id="page">
<?php echo getNavBarHTML() ?> <?php echo getNavBarHTML() ?>
<div id="header">
<div id="info"> <!-- Toolbar button placement and styling handled by bootstrap-tables -->
<h2><?php echo sprintf($CLANG['EventCount'], $nEvents, zmVlang($VLANG['Event'], $nEvents)) ?></h2> <div id="toolbar">
<a href="?view=filter<?php echo $filterQuery ?>"><?php echo translate('Filter')?></a> <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>
<a id="refreshLink" href="#"><?php echo translate('Refresh') ?></a> <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>
</div> <button id="tlineBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('ShowTimeline') ?>" ><i class="fa fa-history"></i></button>
<div id="pagination"> <button id="viewBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('View') ?>" disabled><i class="fa fa-binoculars"></i></button>
<?php <button id="archiveBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Archive') ?>" disabled><i class="fa fa-archive"></i></button>
if ( $pagination ) { <button id="unarchiveBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Unarchive') ?>" disabled><i class="fa fa-file-archive-o"></i></button>
?> <button id="editBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Edit') ?>" disabled><i class="fa fa-pencil"></i></button>
<h2 class="pagination hidden-xs"><?php echo $pagination ?></h2> <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>
<?php <button id="downloadBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('DownloadVideo') ?>" disabled><i class="fa fa-download"></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>
?>
<?php
if ( $pages > 1 ) {
if ( !empty($page) ) {
?>
<a href="?view=<?php echo $view ?>&amp;page=0<?php echo $filterQuery.$sortQuery.$limitQuery ?>"><?php echo translate('ViewAll') ?></a>
<?php
} else {
?>
<a href="?view=<?php echo $view ?>&amp;page=1<?php echo $filterQuery.$sortQuery.$limitQuery ?>"><?php echo translate('ViewPaged') ?></a>
<?php
}
}
?>
</div>
<div id="controls">
<a href="#" id="backLink"><?php echo translate('Back') ?></a>
<a id="timelineLink" href="?view=timeline<?php echo $filter->query() ?>"><?php echo translate('ShowTimeline') ?></a>
</div>
</div> </div>
<div id="content">
<form name="contentForm" id="contentForm" method="post" action=""> <!-- Table styling handled by bootstrap-tables -->
<input type="hidden" name="view" value="<?php echo $view ?>"/> <div class="table-responsive-sm p-3">
<input type="hidden" name="action" value=""/> <table
<input type="hidden" name="page" value="<?php echo $page ?>"/> id="eventTable"
<?php echo $filter->fields() ?> data-toggle="table"
<input type="hidden" name="sort_field" value="<?php echo validHtmlStr($_REQUEST['sort_field']) ?>"/> data-pagination="true"
<input type="hidden" name="sort_asc" value="<?php echo validHtmlStr($_REQUEST['sort_asc']) ?>"/> data-show-pagination-switch="true"
<input type="hidden" name="limit" value="<?php echo $limit ?>"/> data-page-list="[10, 25, 50, 100, 200, All]"
<div class="table-responsive-sm"> data-search="true"
<table id="contentTable" class="major"> data-cookie="true"
<tbody> data-cookie-id-table="zmEventsTable"
data-cookie-expire="2y"
data-click-to-select="true"
data-remember-order="true"
data-show-columns="true"
data-uncheckAll="true"
data-toolbar="#toolbar"
data-show-fullscreen="true"
data-click-to-select="true"
data-maintain-meta-data="true"
data-mobile-responsive="true"
data-buttons-class="btn btn-normal"
class="table-sm table-borderless">
<thead>
<?php <?php
$count = 0; $count = 0;
$disk_space_total = 0; $disk_space_total = 0;
@ -158,49 +152,51 @@ $disk_space_total = 0;
$results = dbQuery($eventsSql); $results = dbQuery($eventsSql);
while ( $event_row = dbFetchNext($results) ) { while ( $event_row = dbFetchNext($results) ) {
$event = new ZM\Event($event_row); $event = new ZM\Event($event_row);
if ( $event_row['Archived'] )
$archived = true;
else
$unarchived = true;
if ( ($count++%ZM_WEB_EVENTS_PER_PAGE) == 0 ) { if ( ($count++%ZM_WEB_EVENTS_PER_PAGE) == 0 ) {
?> ?>
<!-- Row styling is handled by bootstrap-tables -->
<tr> <tr>
<th class="colId"><a href="<?php echo sortHeader('Id') ?>"><?php echo translate('Id') ?><?php echo sortTag('Id') ?></a></th> <th data-sortable="false" data-field="toggleCheck" data-checkbox="true"></th>
<th class="colName px-1"><a href="<?php echo sortHeader('Name') ?>"><?php echo translate('Name') ?><?php echo sortTag('Name') ?></a></th> <th data-sortable="true" data-field="Id"><?php echo translate('Id') ?></th>
<th class="colMonitor px-1"><a href="<?php echo sortHeader('MonitorName') ?>"><?php echo translate('Monitor') ?><?php echo sortTag('MonitorName') ?></a></th> <th data-sortable="true" data-field="Name"><?php echo translate('Name') ?></th>
<th class="colCause px-1"><a href="<?php echo sortHeader('Cause') ?>"><?php echo translate('Cause') ?><?php echo sortTag('Cause') ?></a></th> <th data-sortable="true" data-field="Archived"><?php echo translate('Archived') ?></th>
<th class="colStartTime px-1"><a href="<?php echo sortHeader('StartTime') ?>"><?php echo translate('AttrStartTime') ?><?php echo sortTag('StartTime') ?></a></th> <th data-sortable="true" data-field="Emailed"><?php echo translate('Emailed') ?></th>
<th class="colEndTime px-1"><a href="<?php echo sortHeader('EndTime') ?>"><?php echo translate('AttrEndTime') ?><?php echo sortTag('EndTime') ?></a></th> <th data-sortable="true" data-field="Monitor"><?php echo translate('Monitor') ?></th>
<th class="colDuration px-1"><a href="<?php echo sortHeader('Length') ?>"><?php echo translate('Duration') ?><?php echo sortTag('Length') ?></a></th> <th data-sortable="true" data-field="Cause"><?php echo translate('Cause') ?></th>
<th class="colFrames px-1"><a href="<?php echo sortHeader('Frames') ?>"><?php echo translate('Frames') ?><?php echo sortTag('Frames') ?></a></th> <th data-sortable="true" data-field="AttrStartTime"><?php echo translate('AttrStartTime') ?></th>
<th class="colAlarmFrames px-1"><a href="<?php echo sortHeader('AlarmFrames') ?>"><?php echo translate('AlarmBrFrames') ?><?php echo sortTag('AlarmFrames') ?></a></th> <th data-sortable="true" data-field="AttrEndTime"><?php echo translate('AttrEndTime') ?></th>
<th class="colTotScore px-1"><a href="<?php echo sortHeader('TotScore') ?>"><?php echo translate('TotalBrScore') ?><?php echo sortTag('TotScore') ?></a></th> <th data-sortable="true" data-field="Duration"><?php echo translate('Duration') ?></th>
<th class="colAvgScore px-1"><a href="<?php echo sortHeader('AvgScore') ?>"><?php echo translate('AvgBrScore') ?><?php echo sortTag('AvgScore') ?></a></th> <th data-sortable="true" data-field="Frames"><?php echo translate('Frames') ?></th>
<th class="colMaxScore px-1"><a href="<?php echo sortHeader('MaxScore') ?>"><?php echo translate('MaxBrScore') ?><?php echo sortTag('MaxScore') ?></a></th> <th data-sortable="true" data-field="AlarmBrFrames"><?php echo translate('AlarmBrFrames') ?></th>
<th data-sortable="true" data-field="TotalBrScore"><?php echo translate('TotalBrScore') ?></th>
<th data-sortable="true" data-field="AvgBrScore"><?php echo translate('AvgBrScore') ?></th>
<th data-sortable="true" data-field="MaxBrScore"><?php echo translate('MaxBrScore') ?></th>
<?php <?php
if ( count($storage_areas) > 1 ) { if ( count($storage_areas) > 1 ) {
?> ?>
<th class="colStorage px-1"><?php echo translate('Storage') ?></th> <th data-sortable="true" data-field="Storage"><?php echo translate('Storage') ?></th>
<?php <?php
} }
if ( ZM_WEB_EVENT_DISK_SPACE ) { if ( ZM_WEB_EVENT_DISK_SPACE ) {
?> ?>
<th class="colDiskSpace px-1"><a href="<?php echo sortHeader('DiskSpace') ?>"><?php echo translate('DiskSpace') ?><?php echo sortTag('DiskSpace') ?></a></th> <th data-sortable="true" data-field="DiskSpace"><?php echo translate('DiskSpace') ?></th>
<?php <?php
} }
if ( ZM_WEB_LIST_THUMBS ) { if ( ZM_WEB_LIST_THUMBS ) {
?> ?>
<th class="colThumbnail px-1"><?php echo translate('Thumbnail') ?></th> <th data-sortable="false" data-field="Thumbnail"><?php echo translate('Thumbnail') ?></th>
<?php <?php
} }
?> ?>
<th class="colMark px-1"><input type="checkbox" name="toggleCheck" value="1" data-checkbox-name="eids[]" data-on-click-this="updateFormCheckboxesByName"/></th>
</tr> </tr>
</thead>
<tbody>
<?php <?php
} }
$scale = max( reScale( SCALE_BASE, $event->DefaultScale(), ZM_WEB_DEFAULT_SCALE ), SCALE_BASE ); $scale = max( reScale( SCALE_BASE, $event->DefaultScale(), ZM_WEB_DEFAULT_SCALE ), SCALE_BASE );
?> ?>
<<<<<<< HEAD
<tr<?php if ($event->Archived()) echo ' class="archived"' ?>> <tr<?php if ($event->Archived()) echo ' class="archived"' ?>>
<td class="colId"><a href="?view=event&amp;eid=<?php echo $event->Id().$filterQuery.$sortQuery.'&amp;page=1">'.$event->Id().($event->Archived()?'*':'') ?></a></td> <td class="colId"><a href="?view=event&amp;eid=<?php echo $event->Id().$filterQuery.$sortQuery.'&amp;page=1">'.$event->Id().($event->Archived()?'*':'') ?></a></td>
<td class="colName px-1"><a href="?view=event&amp;eid=<?php echo $event->Id().$filterQuery.$sortQuery.'&amp;page=1">'.validHtmlStr($event->Name()).($event->Archived()?'*':'') ?></a><br/> <td class="colName px-1"><a href="?view=event&amp;eid=<?php echo $event->Id().$filterQuery.$sortQuery.'&amp;page=1">'.validHtmlStr($event->Name()).($event->Archived()?'*':'') ?></a><br/>
@ -225,26 +221,55 @@ while ( $event_row = dbFetchNext($results) ) {
} }
} }
?> ?>
=======
<tr<?php echo ( $event->Archived() ) ? ' class="archived"' : '' ?>>
<td data-checkbox="true"></td>
<td><a href="?view=event&amp;eid=<?php echo $event->Id().$filterQuery.$sortQuery.'&amp;page=1">'.$event->Id() ?></a></td>
<td><a href="?view=event&amp;eid=<?php echo $event->Id().$filterQuery.$sortQuery.'&amp;page=1">'.validHtmlStr($event->Name())?></a>
<?php
$archived = ( $event->Archived() ) ? translate('Archived') : '';
$emailed = ( $event->Emailed() ) ? ' '.translate('Emailed') : '';
echo '<br/><div class="small text-nowrap text-muted">'.$archived.$emailed.'</div>';
?>
>>>>>>> bec77c9ce3b185ba3055e23cbba88d9c420c66c7
</td> </td>
<td class="colTime text-wrap px-1"><?php echo strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->StartTime())) ?></td>
<td class="colTime text-wrap px-1"><?php echo strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->EndTime()) ) ?></td> <td class="text-center"><?php echo ( $event->Archived() ) ? 'Yes' : 'No' ?></td>
<td class="colDuration px-1"><?php echo gmdate("H:i:s", $event->Length() ) ?></td> <td class="text-center"><?php echo ( $event->Emailed() ) ? 'Yes' : 'No' ?></td>
<td class="colFrames px-1"><?php echo makePopupLink( '?view=frames&amp;eid='.$event->Id(), 'zmFrames', <td><?php echo makePopupLink( '?view=monitor&amp;mid='.$event->MonitorId(), 'zmMonitor'.$event->MonitorId(), 'monitor', $event->MonitorName(), canEdit( 'Monitors' ) ) ?></td>
( ZM_WEB_LIST_THUMBS ? array('frames', ZM_WEB_LIST_THUMB_WIDTH, ZM_WEB_LIST_THUMB_HEIGHT) : 'frames'), <td><?php echo makePopupLink( '?view=eventdetail&amp;eid='.$event->Id(), 'zmEventDetail', 'eventdetail', validHtmlStr($event->Cause()), canEdit( 'Events' ), 'title="'.htmlspecialchars($event->Notes()).'"' ) ?>
$event->Frames() ) ?></td> <?php
<td class="colAlarmFrames px-1"><?php echo makePopupLink( '?view=frames&amp;eid='.$event->Id(), 'zmFrames', # display notes as small text
( ZM_WEB_LIST_THUMBS ? array('frames', ZM_WEB_LIST_THUMB_WIDTH, ZM_WEB_LIST_THUMB_HEIGHT) : 'frames'), if ( $event->Notes() ) {
$event->AlarmFrames() ) ?></td> # if notes include detection objects, then link it to objdetect.jpg
<td class="colTotScore px-1"><?php echo $event->TotScore() ?></td> if ( strpos($event->Notes(), 'detected:') !== false ) {
<td class="colAvgScore px-1"><?php echo $event->AvgScore() ?></td> # make a link
<td class="colMaxScore px-1"><?php echo makePopupLink( echo makePopupLink( '?view=image&amp;eid='.$event->Id().'&amp;fid=objdetect', 'zmImage',
array('image', reScale($event->Width(), $scale), reScale($event->Height(), $scale)),
'<div class="small text-nowrap text-muted"><u>'.$event->Notes().'</u></div>');
} else if ( $event->Notes() != 'Forced Web: ' ) {
echo '<br/><div class="small text-nowrap text-muted">'.$event->Notes().'</div>';
}
}
?>
</td>
<td><?php echo strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->StartTime())) ?></td>
<td><?php echo strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->EndTime()) ) ?></td>
<td><?php echo gmdate("H:i:s", $event->Length() ) ?></td>
<td><a href="?view=frames&amp;eid=<?php echo $event->Id() ?>"><?php echo $event->Frames() ?></a></td>
<td><a href="?view=frames&amp;eid=<?php echo $event->Id() ?>"><?php echo $event->AlarmFrames() ?></a></td>
<td><?php echo $event->TotScore() ?></td>
<td><?php echo $event->AvgScore() ?></td>
<td><?php echo makePopupLink(
'?view=frame&amp;eid='.$event->Id().'&amp;fid=0', 'zmImage', '?view=frame&amp;eid='.$event->Id().'&amp;fid=0', 'zmImage',
array('image', reScale($event->Width(), $scale), reScale($event->Height(), $scale)), $event->MaxScore() array('image', reScale($event->Width(), $scale), reScale($event->Height(), $scale)), $event->MaxScore()
); ?></td> ); ?></td>
<?php <?php
if ( count($storage_areas) > 1 ) { if ( count($storage_areas) > 1 ) {
?> ?>
<td class="colStorage px-1"> <td>
<?php <?php
if ( $event->StorageId() ) { if ( $event->StorageId() ) {
echo isset($StorageById[$event->StorageId()]) ? $StorageById[$event->StorageId()]->Name() : 'Unknown Storage Id: '.$event->StorageId(); echo isset($StorageById[$event->StorageId()]) ? $StorageById[$event->StorageId()]->Name() : 'Unknown Storage Id: '.$event->StorageId();
@ -262,11 +287,11 @@ while ( $event_row = dbFetchNext($results) ) {
if ( ZM_WEB_EVENT_DISK_SPACE ) { if ( ZM_WEB_EVENT_DISK_SPACE ) {
$disk_space_total += $event->DiskSpace(); $disk_space_total += $event->DiskSpace();
?> ?>
<td class="colDiskSpace px-1"><?php echo human_filesize($event->DiskSpace()) ?></td> <td class="colDiskSpace"><?php echo human_filesize($event->DiskSpace()) ?></td>
<?php <?php
} }
if ( ZM_WEB_LIST_THUMBS ) { if ( ZM_WEB_LIST_THUMBS ) {
echo '<td class="colThumbnail px-1 zoom">'; echo '<td class="colThumbnail zoom">';
$imgSrc = $event->getThumbnailSrc(array(),'&amp;'); $imgSrc = $event->getThumbnailSrc(array(),'&amp;');
$streamSrc = $event->getStreamSrc(array( $streamSrc = $event->getStreamSrc(array(
'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single', 'rate'=>'400'), '&amp;'); 'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single', 'rate'=>'400'), '&amp;');
@ -276,7 +301,6 @@ while ( $event_row = dbFetchNext($results) ) {
echo '</td>'; echo '</td>';
} // end if ZM_WEB_LIST_THUMBS } // end if ZM_WEB_LIST_THUMBS
?> ?>
<td class="colMark px-1"><input type="checkbox" name="eids[]" value="<?php echo $event->Id() ?>"/></td>
</tr> </tr>
<?php <?php
} }
@ -310,45 +334,29 @@ while ( $event_row = dbFetchNext($results) ) {
} }
?> ?>
</table> </table>
</div> </div>
<?php </div>
if ( $pagination ) {
?> <!-- Delete Confirmation Modal -->
<h3 class="pagination"><?php echo $pagination ?></h3> <div id="deleteConfirm" class="modal fade" class="modal" tabindex="-1">
<?php <div class="modal-dialog">
} <div class="modal-content">
?> <div class="modal-header">
<div id="contentButtons"> <h5 class="modal-title">Delete Confirmation</h5>
<button type="button" name="viewBtn" value="View" data-on-click-this="viewEvents" disabled="disabled"> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<?php echo translate('View') ?> <span aria-hidden="true">&times;</span>
</button> </button>
<button type="button" name="archiveBtn" value="Archive" data-on-click-this="archiveEvents" disabled="disabled"> </div>
<?php echo translate('Archive') ?> <div class="modal-body">
</button> <p><?php echo translate('ConfirmDeleteEvents') ?></p>
<button type="button" name="unarchiveBtn" value="Unarchive" data-on-click-this="unarchiveEvents" disabled="disabled"> </div>
<?php echo translate('Unarchive') ?> <div class="modal-footer">
</button> <button id="delCancelBtn" type="button" class="btn btn-secondary" data-dismiss="modal"><?php echo translate('Cancel') ?></button>
<button type="button" name="editBtn" value="Edit" data-on-click-this="editEvents" disabled="disabled"> <button id ="delConfirmBtn" type="button" class="btn btn-danger"><?php echo translate('Delete') ?></button>
<?php echo translate('Edit') ?> </div>
</button>
<button type="button" name="exportBtn" value="Export" data-on-click-this="exportEvents" disabled="disabled">
<?php echo translate('Export') ?>
</button>
<button type="button" name="downloadBtn" value="DownloadVideo" data-on-click-this="downloadVideo" disabled="disabled">
<?php echo translate('DownloadVideo') ?>
</button>
<button type="button" name="deleteBtn" value="Delete" data-on-click-this="deleteEvents" disabled="disabled">
<?php echo translate('Delete') ?>
</button>
</div>
</form>
</div> </div>
</div> </div>
<script nonce="<?php echo $cspNonce;?>"> </div>
// These are defined in the .js.php but need to be updated down here.
// This might be better done by selecting through the dom for the archived class
archivedEvents = <?php echo !empty($archived)?'true':'false' ?>;
unarchivedEvents = <?php echo !empty($unarchived)?'true':'false' ?>;
</script>
</body> </body>
</html> </html>

View File

@ -86,6 +86,7 @@ xhtmlHeaders(__FILE__, translate('Frame').' - '.$Event->Id().' - '.$Frame->Frame
?> ?>
<body> <body>
<div id="page"> <div id="page">
<?php echo getNavBarHTML() ?>
<div id="header"> <div id="header">
<form> <form>
<div id="headerButtons"> <div id="headerButtons">

View File

@ -109,100 +109,78 @@ $focusWindow = true;
xhtmlHeaders(__FILE__, translate('Frames').' - '.$Event->Id()); xhtmlHeaders(__FILE__, translate('Frames').' - '.$Event->Id());
?> ?>
<body> <body>
<div id="page"> <div id="page" class="container-fluid p-0">
<div id="header"> <?php echo getNavBarHTML() ?>
<div id="headerButtons"><a href="#" data-on-click="closeWindow"><?php echo translate('Close') ?></a></div>
<h2><?php echo translate('Frames') ?> - <?php echo $Event->Id() ?></h2> <!-- Toolbar button placement and styling handled by bootstrap-tables -->
<div id="pagination"> <div id="toolbar">
<?php <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>
if ( $pagination ) { <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>
?>
<h2 class="pagination"><?php echo $pagination ?></h2>
<?php
}
?>
<?php
if ( $pages > 1 ) {
if ( !empty($page) ) {
?>
<a href="?view=<?php echo $view ?>&amp;page=0<?php echo $totalQuery ?>"><?php echo translate('ViewAll') ?></a>
<?php
} else {
?>
<a href="?view=<?php echo $view ?>&amp;page=1<?php echo $totalQuery ?>"><?php echo translate('ViewPaged') ?></a>
<?php
}
}
?>
</div>
</div> </div>
<div id="content">
<form name="contentForm" id="contentForm" method="get" action="?"> <!-- Table styling handled by bootstrap-tables -->
<input type="hidden" name="view" value="none"/> <div class="row justify-content-center">
<input type="hidden" name="view" value="<?php echo $view ?>"/> <table
<input type="hidden" name="action" value=""/> id="framesTable"
<input type="hidden" name="page" value="<?php echo $page ?>"/> data-toggle="table"
<input type="hidden" name="eid" value="<?php echo $eid ?>"/> data-pagination="true"
<?php echo $_REQUEST['filter']['fields'] ?> data-show-pagination-switch="true"
<input type="hidden" name="sort_field" value="<?php echo validHtmlStr($_REQUEST['sort_field']) ?>"/> data-page-list="[10, 25, 50, 100, 200, All]"
<input type="hidden" name="sort_asc" value="<?php echo validHtmlStr($_REQUEST['sort_asc']) ?>"/> data-search="true"
<input type="hidden" name="limit" value="<?php echo $limit ?>"/> data-cookie="true"
<table id="contentTable" class="major"> data-cookie-id-table="zmFramesTable"
<thead> data-cookie-expire="2y"
<tr> data-remember-order="true"
<th class="colId"><a href="<?php echo sortHeader('FramesFrameId') ?>"><?php echo translate('Frame Id') ?><?php echo sortTag('FramesFrameId') ?></a></th> data-show-columns="true"
<th class="colType"><a href="<?php echo sortHeader('FramesType') ?>"><?php echo translate('Type') ?><?php echo sortTag('FramesType') ?></a></th> data-toolbar="#toolbar"
<th class="colTimeStamp"><a href="<?php echo sortHeader('FramesTimeStamp') ?>"><?php echo translate('TimeStamp') ?><?php echo sortTag('FramesTimeStamp') ?></a></th> data-show-fullscreen="true"
<th class="colTimeDelta"><a href="<?php echo sortHeader('FramesDelta') ?>"><?php echo translate('TimeDelta') ?><?php echo sortTag('FramesDelta') ?></a></th> data-maintain-meta-data="true"
<th class="colScore"><a href="<?php echo sortHeader('FramesScore') ?>"><?php echo translate('Score') ?><?php echo sortTag('FramesScore') ?></a></th> data-mobile-responsive="true"
data-buttons-class="btn btn-normal"
class="table-sm table-borderless">
<thead>
<!-- Row styling is handled by bootstrap-tables -->
<tr>
<th data-align="center" data-sortable="false" data-field="EventId"><?php echo translate('EventId') ?></th>
<th data-align="center" data-sortable="true" data-field="FramesId"><?php echo translate('FrameId') ?></th>
<th data-align="center" data-sortable="true" data-field="FramesType"><?php echo translate('Type') ?></th>
<th data-align="center" data-sortable="true" data-field="FramesTimeStamp"><?php echo translate('TimeStamp') ?></th>
<th data-align="center" data-sortable="true" data-field="FramesDelta"><?php echo translate('TimeDelta') ?></th>
<th data-align="center" data-sortable="true" data-field="FramesScore"><?php echo translate('Score') ?></th>
<?php <?php
if ( ZM_WEB_LIST_THUMBS ) { if ( ZM_WEB_LIST_THUMBS ) {
?> ?>
<th class="colThumbnail"><?php echo translate('Thumbnail') ?></th> <th data-align="center" data-sortable="false" data-field="Thumbnail"><?php echo translate('Thumbnail') ?></th>
<?php <?php
} }
?> ?>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php <?php
if ( count($frames) ) { if ( count($frames) ) {
foreach ( $frames as $frame ) { foreach ( $frames as $frame ) {
$Frame = new ZM\Frame($frame); $Frame = new ZM\Frame($frame);
$class = strtolower($frame['Type']);
?> ?>
<tr class="<?php echo $class ?>"> <tr<?php echo ( strtolower($frame['Type']) == "alarm" ) ? ' class="alarm"' : '' ?>>
<td class="colId"><?php echo makePopupLink( <td><?php echo $frame['EventId'] ?></td>
'?view=frame&amp;eid='.$Event->Id().'&amp;fid='.$frame['FrameId'], 'zmImage', <td><?php echo $frame['FrameId'] ?></td>
array( <td><?php echo $frame['Type'] ?></td>
'frame', <td><?php echo strftime(STRF_FMT_TIME, $frame['UnixTimeStamp']) ?></td>
($scale ? $Event->Width()*$scale/100 : $Event->Width()), <td><?php echo number_format( $frame['Delta'], 2 ) ?></td>
($scale ? $Event->Height()*$scale/100 : $Event->Height()) <td><?php echo $frame['Score'] ?></td>
),
$frame['FrameId'])
?></td>
<td class="colType"><?php echo $frame['Type'] ?></td>
<td class="colTimeStamp"><?php echo strftime(STRF_FMT_TIME, $frame['UnixTimeStamp']) ?></td>
<td class="colTimeDelta"><?php echo number_format( $frame['Delta'], 2 ) ?></td>
<?php <?php
if ( ZM_RECORD_EVENT_STATS && ($frame['Type'] == 'Alarm') ) {
?>
<td class="colScore"><?php echo makePopupLink('?view=stats&amp;eid='.$Event->Id().'&amp;fid='.$frame['FrameId'], 'zmStats', 'stats', $frame['Score']) ?></td>
<?php
} else {
?>
<td class="colScore"><?php echo $frame['Score'] ?></td>
<?php
}
if ( ZM_WEB_LIST_THUMBS ) { if ( ZM_WEB_LIST_THUMBS ) {
?> $base_img_src = '?view=image&amp;fid=' .$Frame->Id();
<td class="colThumbnail"><?php echo makePopupLink( '?view=frame&amp;eid='.$Event->Id().'&amp;fid='.$frame['FrameId'], 'zmImage', array('image', $Event->Width(), $Event->Height()), '<img src="?view=image&amp;fid='.$Frame->Id().'&amp;'. $thmb_width = ZM_WEB_LIST_THUMB_WIDTH ? 'width='.ZM_WEB_LIST_THUMB_WIDTH : '';
(ZM_WEB_LIST_THUMB_WIDTH?'width='.ZM_WEB_LIST_THUMB_WIDTH.'&amp;':''). $thmb_height = ZM_WEB_LIST_THUMB_HEIGHT ? 'height='.ZM_WEB_LIST_THUMB_HEIGHT : '';
(ZM_WEB_LIST_THUMB_HEIGHT?'height='.ZM_WEB_LIST_THUMB_HEIGHT.'&amp;':'').'filename='.$Event->MonitorId().'_'.$frame['EventId'].'_'.$frame['FrameId'].'.jpg" '. $thmb_fn = 'filename=' .$Event->MonitorId(). '_' .$frame['EventId']. '_' .$frame['FrameId']. '.jpg';
(ZM_WEB_LIST_THUMB_WIDTH?'width="'.ZM_WEB_LIST_THUMB_WIDTH.'" ':''). $img_src = join('&amp;', array_filter(array($base_img_src, $thmb_width, $thmb_height, $thmb_fn)));
(ZM_WEB_LIST_THUMB_HEIGHT?'height="'.ZM_WEB_LIST_THUMB_HEIGHT.'" ':'').' alt="'.$frame['FrameId'].'"/>' ) ?></td> $full_img_src = join('&amp;', array_filter(array($base_img_src, $thmb_fn)));
<?php $frame_src = '?view=frame&amp;eid=' .$Event->Id(). '&amp;fid=' .$frame['FrameId'];
echo '<td class="colThumbnail zoom"><img src="' .$img_src. '" '.$thmb_width. ' ' .$thmb_height. 'img_src="' .$img_src. '" full_img_src="' .$full_img_src. '"></td>'.PHP_EOL;
} }
?> ?>
</tr> </tr>
@ -218,19 +196,7 @@ if ( count($frames) ) {
?> ?>
</tbody> </tbody>
</table> </table>
</div>
<?php
if ( $pagination ) {
?>
<h3 class="pagination"><?php echo $pagination ?></h3>
<?php
}
?>
<div id="contentButtons">
</div>
</form>
</div>
</div> </div>
</body> </body>
</html> </html>

View File

@ -1,16 +1,19 @@
<?php <?php
if ( isset($_REQUEST['eids']) ) { global $connkey;
$eidParms = array(); global $exportFormat;
foreach ( $_REQUEST['eids'] as $eid )
$eidParms[] = 'eids[]='.validInt($eid); if ( isset($_REQUEST['eids']) ) {
$eidParms = array();
foreach ( $_REQUEST['eids'] as $eid )
$eidParms[] = 'eids[]='.validInt($eid);
?> ?>
var eidParm = '<?php echo join('&', $eidParms) ?>'; var eidParm = '<?php echo join('&', $eidParms) ?>';
<?php <?php
} else if (isset($_REQUEST['eid'])) { } else if (isset($_REQUEST['eid'])) {
?> ?>
var eidParm = 'eid=<?php echo validInt($_REQUEST['eid']) ?>'; var eidParm = 'eid=<?php echo validInt($_REQUEST['eid']) ?>';
<?php <?php
} }
?> ?>
var exportReady = <?php echo !empty($_REQUEST['generated'])?'true':'false' ?>; var exportReady = <?php echo !empty($_REQUEST['generated'])?'true':'false' ?>;

View File

@ -1,179 +1,212 @@
function closeWindows() {
window.close();
// This is a hack. The only way to close an existing window is to try and open it!
var filterWindow = window.open( thisUrl+'?view=none', 'zmFilter', 'width=1,height=1' );
filterWindow.close();
}
function setButtonStates( element ) {
var form = element.form;
var checked = element.checked;
form.viewBtn.disabled = !(canViewEvents && checked);
form.editBtn.disabled = !(canEditEvents && checked);
form.archiveBtn.disabled = unarchivedEvents?!checked:true;
form.unarchiveBtn.disabled = !(canEditEvents && archivedEvents && checked);
form.downloadBtn.disabled = !(canViewEvents && checked);
form.exportBtn.disabled = !(canViewEvents && checked);
form.deleteBtn.disabled = !(canEditEvents && checked);
}
function configureButton(event) {
var element = event.target;
var form = element.form;
var checked = element.checked;
if ( !checked ) {
for (var i = 0, len=form.elements.length; i < len; i++) {
if ( form.elements[i].name.indexOf('eids') == 0) {
if ( form.elements[i].checked ) {
checked = true;
break;
}
}
}
}
if ( !element.checked ) {
form.toggleCheck.checked = false;
}
form.viewBtn.disabled = !(canViewEvents && checked);
form.editBtn.disabled = !(canEditEvents && checked);
form.archiveBtn.disabled = (!checked)||(!unarchivedEvents);
form.unarchiveBtn.disabled = !(canEditEvents && checked && archivedEvents);
form.downloadBtn.disabled = !(canViewEvents && checked);
form.exportBtn.disabled = !(canViewEvents && checked);
form.deleteBtn.disabled = !(canEditEvents && checked);
}
function deleteEvents( element ) {
if ( ! canEditEvents ) {
alert("You do not have permission to delete events.");
return;
}
var form = element.form;
var count = 0;
// This is slightly more efficient than a jquery selector because we stop after finding one.
for (var i = 0; i < form.elements.length; i++) {
if (form.elements[i].name.indexOf('eids') == 0) {
if ( form.elements[i].checked ) {
count++;
break;
}
}
}
if ( count > 0 ) {
if ( confirm(confirmDeleteEventsString) ) {
form.elements['action'].value = 'delete';
form.submit();
}
}
}
function editEvents( element ) {
if ( ! canEditEvents ) {
alert("You do not have permission to delete events.");
return;
}
var form = element.form;
var eids = new Array();
for (var i = 0, len=form.elements.length; i < len; i++) {
if (form.elements[i].name.indexOf('eids') == 0) {
if ( form.elements[i].checked ) {
eids[eids.length] = 'eids[]='+form.elements[i].value;
}
}
}
createPopup('?view=eventdetail&'+eids.join('&'), 'zmEventDetail', 'eventdetail');
}
function downloadVideo( element ) {
var form = element.form;
var eids = new Array();
for (var i = 0, len=form.elements.length; i < len; i++) {
if (form.elements[i].name.indexOf('eids') == 0 ) {
if ( form.elements[i].checked ) {
eids[eids.length] = 'eids[]='+form.elements[i].value;
}
}
}
createPopup( '?view=download&'+eids.join('&'), 'zmDownload', 'download' );
}
function exportEvents( element ) {
var form = element.form;
console.log(form);
form.action = '?view=export';
form.elements['view'].value='export';
form.submit();
}
function viewEvents( element ) {
var form = element.form;
var events = new Array();
for (var i = 0, len=form.elements.length; i < len; i++) {
if ( form.elements[i].name.indexOf('eids') == 0 ) {
if ( form.elements[i].checked ) {
events[events.length] = form.elements[i].value;
}
}
}
if ( events.length > 0 ) {
var filter = '&filter[Query][terms][0][attr]=Id&filter[Query][terms][0][op]=%3D%5B%5D&filter[Query][terms][0][val]='+events.join('%2C');
window.location.href = thisUrl+'?view=event&eid='+events[0]+filter+sortQuery+'&page=1&play=1';
}
}
function archiveEvents(element) {
var form = element.form;
form.elements['action'].value = 'archive';
form.submit();
}
function unarchiveEvents(element) {
if ( ! canEditEvents ) {
alert("You do not have permission to delete events.");
return;
}
var form = element.form;
form.elements['action'].value = 'unarchive';
form.submit();
}
if ( openFilterWindow ) {
//opener.location.reload(true);
createPopup( '?view=filter&page='+thisPage+filterQuery, 'zmFilter', 'filter' );
location.replace( '?view='+currentView+'&page='+thisPage+filterQuery );
}
function thumbnail_onmouseover(event) { function thumbnail_onmouseover(event) {
var img = event.target; var img = event.target;
img.src = ''; img.src = '';
img.src = img.getAttribute('stream_src'); img.src = img.getAttribute('stream_src');
} }
function thumbnail_onmouseout(event) { function thumbnail_onmouseout(event) {
var img = event.target; var img = event.target;
img.src = ''; img.src = '';
img.src = img.getAttribute('still_src'); img.src = img.getAttribute('still_src');
} }
function initPage() { function initThumbAnimation() {
if ( window.history.length == 1 ) {
$j('#controls').children().eq(0).html('');
}
$j('.colThumbnail img').each(function() { $j('.colThumbnail img').each(function() {
this.addEventListener('mouseover', thumbnail_onmouseover, false); this.addEventListener('mouseover', thumbnail_onmouseover, false);
this.addEventListener('mouseout', thumbnail_onmouseout, false); this.addEventListener('mouseout', thumbnail_onmouseout, false);
}); });
$j('input[name=eids\\[\\]]').each(function() { }
this.addEventListener('click', configureButton, false);
}); // Returns the event id's of the selected rows
document.getElementById("refreshLink").addEventListener("click", function onRefreshClick(evt) { function getIdSelections() {
evt.preventDefault(); var table = $j('#eventTable');
window.location.reload(true);
}); return $j.map(table.bootstrapTable('getSelections'), function(row) {
document.getElementById("backLink").addEventListener("click", function onBackClick(evt) { return row.Id.replace(/(<([^>]+)>)/gi, ""); // strip the html from the element before sending
evt.preventDefault();
window.history.back();
}); });
} }
$j(document).ready(initPage); // Returns a boolen to indicate at least one selected row is archived
function getArchivedSelections() {
var table = $j('#eventTable');
var selection = $j.map(table.bootstrapTable('getSelections'), function(row) {
return row.Archived;
});
return selection.includes("Yes");
}
function initPage() {
var backBtn = $j('#backBtn');
var viewBtn = $j('#viewBtn');
var archiveBtn = $j('#archiveBtn');
var unarchiveBtn = $j('#unarchiveBtn');
var editBtn = $j('#editBtn');
var exportBtn = $j('#exportBtn');
var downloadBtn = $j('#downloadBtn');
var deleteBtn = $j('#deleteBtn');
var table = $j('#eventTable');
// Define the icons used in the bootstrap-table top-right toolbar
var icons = {
paginationSwitchDown: 'fa-caret-square-o-down',
paginationSwitchUp: 'fa-caret-square-o-up',
refresh: 'fa-sync',
toggleOff: 'fa-toggle-off',
toggleOn: 'fa-toggle-on',
columns: 'fa-th-list',
fullscreen: 'fa-arrows-alt',
detailOpen: 'fa-plus',
detailClose: 'fa-minus'
};
// Init the bootstrap-table
table.bootstrapTable('destroy').bootstrapTable({icons: icons});
// Hide these columns on first run when no cookie is saved
if ( !getCookie("zmEventsTable.bs.table.columns") ) {
table.bootstrapTable('hideColumn', 'Archived');
table.bootstrapTable('hideColumn', 'Emailed');
}
// 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() {
viewBtn.prop('disabled', !(table.bootstrapTable('getSelections').length && canViewEvents));
archiveBtn.prop('disabled', !(table.bootstrapTable('getSelections').length && canEditEvents));
unarchiveBtn.prop('disabled', !(getArchivedSelections()) && canEditEvents);
editBtn.prop('disabled', !(table.bootstrapTable('getSelections').length && canEditEvents));
exportBtn.prop('disabled', !(table.bootstrapTable('getSelections').length && canViewEvents));
downloadBtn.prop('disabled', !(table.bootstrapTable('getSelections').length && canViewEvents));
deleteBtn.prop('disabled', !(table.bootstrapTable('getSelections').length && canEditEvents));
});
// Don't enable the back button if there is no previous zm page to go back to
backBtn.prop('disabled', !document.referrer.length);
// Setup the thumbnail video animation
initThumbAnimation();
// Some toolbar events break the thumbnail animation, so re-init eventlistener
table.on('all.bs.table', initThumbAnimation);
// Manage the BACK button
document.getElementById("backBtn").addEventListener("click", function onBackClick(evt) {
evt.preventDefault();
window.history.back();
});
// Manage the REFRESH Button
document.getElementById("refreshBtn").addEventListener("click", function onRefreshClick(evt) {
evt.preventDefault();
window.location.reload(true);
});
// Manage the TIMELINE Button
document.getElementById("tlineBtn").addEventListener("click", function onTlineClick(evt) {
evt.preventDefault();
window.location.assign('?view=timeline'+filterQuery);
});
// Manage the VIEW button
document.getElementById("viewBtn").addEventListener("click", function onViewClick(evt) {
var selections = getIdSelections();
evt.preventDefault();
var filter = '&filter[Query][terms][0][attr]=Id&filter[Query][terms][0][op]=%3D%5B%5D&filter[Query][terms][0][val]='+selections.join('%2C');
window.location.href = thisUrl+'?view=event&eid='+selections[0]+filter+sortQuery+'&page=1&play=1';
});
// Manage the ARCHIVE button
document.getElementById("archiveBtn").addEventListener("click", function onArchiveClick(evt) {
var selections = getIdSelections();
evt.preventDefault();
$j.getJSON(thisUrl + '?view=events&action=archive&eids[]='+selections.join('&eids[]='));
window.location.reload(true);
});
// Manage the UNARCHIVE button
document.getElementById("unarchiveBtn").addEventListener("click", function onUnarchiveClick(evt) {
if ( ! canEditEvents ) {
alert("You do not have permission to Unarchive events.");
return;
}
var selections = getIdSelections();
evt.preventDefault();
$j.getJSON(thisUrl + '?view=events&action=unarchive&eids[]='+selections.join('&eids[]='));
if ( openFilterWindow ) {
//opener.location.reload(true);
createPopup( '?view=filter&page='+thisPage+filterQuery, 'zmFilter', 'filter' );
location.replace( '?view='+currentView+'&page='+thisPage+filterQuery );
} else {
window.location.reload(true);
}
});
// Manage the EDIT button
document.getElementById("editBtn").addEventListener("click", function onEditClick(evt) {
if ( ! canEditEvents ) {
alert("You do not have permission to edit events.");
return;
}
var selections = getIdSelections();
evt.preventDefault();
createPopup('?view=eventdetail&eids[]='+selections.join('&eids[]='), 'zmEventDetail', 'eventdetail');
});
// Manage the EXPORT button
document.getElementById("exportBtn").addEventListener("click", function onExportClick(evt) {
var selections = getIdSelections();
evt.preventDefault();
window.location.assign('?view=export&eids[]='+selections.join('&eids[]='));
});
// Manage the DOWNLOAD VIDEO button
document.getElementById("downloadBtn").addEventListener("click", function onDownloadClick(evt) {
var selections = getIdSelections();
evt.preventDefault();
createPopup('?view=download&eids[]='+selections.join('&eids[]='), 'zmDownload', 'download');
});
// Manage the DELETE button
document.getElementById("deleteBtn").addEventListener("click", function onDeleteClick(evt) {
if ( ! canEditEvents ) {
alert("You do not have permission to delete events.");
return;
}
evt.preventDefault();
$j('#deleteConfirm').modal('show');
});
// Manage the DELETE CONFIRMATION modal button
document.getElementById("delConfirmBtn").addEventListener("click", function onDelConfirmClick(evt) {
if ( ! canEditEvents ) {
alert("You do not have permission to delete events.");
return;
}
var selections = getIdSelections();
evt.preventDefault();
$j.getJSON(thisUrl + '?view=events&action=delete&eids[]='+selections.join('&eids[]='));
window.location.reload(true);
});
// Manage the CANCEL modal button
document.getElementById("delCancelBtn").addEventListener("click", function onDelCancelClick(evt) {
$j('#deleteConfirm').modal('hide');
});
}
$j(document).ready(function() {
initPage();
});

View File

@ -1,9 +1,6 @@
//var openFilterWindow = <?php echo $_REQUEST['filter']?'true':'false' ?>; //var openFilterWindow = <?php echo $_REQUEST['filter']?'true':'false' ?>;
var openFilterWindow = false; var openFilterWindow = false;
var archivedEvents = <?php echo !empty($archived)?'true':'false' ?>;
var unarchivedEvents = <?php echo !empty($unarchived)?'true':'false' ?>;
var filterQuery = '<?php echo isset($filterQuery)?validJsStr(htmlspecialchars_decode($filterQuery)):'' ?>'; var filterQuery = '<?php echo isset($filterQuery)?validJsStr(htmlspecialchars_decode($filterQuery)):'' ?>';
var sortQuery = '<?php echo isset($sortQuery)?validJsStr(htmlspecialchars_decode($sortQuery)):'' ?>'; var sortQuery = '<?php echo isset($sortQuery)?validJsStr(htmlspecialchars_decode($sortQuery)):'' ?>';

View File

@ -7,7 +7,7 @@ function validateForm( form ) {
var obrCount = 0; var obrCount = 0;
var cbrCount = 0; var cbrCount = 0;
for ( var i = 0; i < rows.length; i++ ) { for ( var i = 0; i < rows.length; i++ ) {
if (rows.length > 2) { if ( rows.length > 2 ) {
obrCount += parseInt(form.elements['filter[Query][terms][' + i + '][obr]'].value); obrCount += parseInt(form.elements['filter[Query][terms][' + i + '][obr]'].value);
cbrCount += parseInt(form.elements['filter[Query][terms][' + i + '][cbr]'].value); cbrCount += parseInt(form.elements['filter[Query][terms][' + i + '][cbr]'].value);
} }
@ -22,9 +22,53 @@ function validateForm( form ) {
} }
var numbers_reg = /\D/; var numbers_reg = /\D/;
if ( numbers_reg.test(form.elements['filter[Query][limit]'].value) ) { if ( numbers_reg.test(form.elements['filter[Query][limit]'].value) ) {
alert("There appear to be non-numeric characters in your limit. Limit must be a positive integer value or empty."); alert('There appear to be non-numeric characters in your limit. Limit must be a positive integer value or empty.');
return false; return false;
} }
if ( form.elements['filter[AutoDelete]'].checked ) {
// if Delete action is Enabled should also have an unarchived term
var have_archivestatus_term = false;
for ( var i = 0; i < rows.length; i++ ) {
if ( form.elements['filter[Query][terms][' + i + '][attr]'].value == 'Archived' ) {
have_archivestatus_term = true;
}
}
if ( ! have_archivestatus_term ) {
return confirm('You have enabled deleting events but do not have a term referencing the archived status of the event. This filter may delete events that you want to save! Are you sure?');
}
} else if ( form.elements['filter[UpdateDiskSpace]'].checked ) {
var have_endtime_term = false;
for ( var i = 0; i < rows.length; i++ ) {
if ( form.elements['filter[Query][terms][' + i + '][attr]'].value == 'EndDateTime' ) {
have_endtime_term = true;
}
}
if ( ! have_endtime_term ) {
return confirm('You don\'t have an EndTime term in your filter. This will match recordings that are still in progress and so the UpdateDiskSpace action will be a waste of time and resources. Ideally you should have an EndTime IS NOT NULL term. Do you want to continue?');
}
} else if ( form.elements['filter[Background]'].checked ) {
if ( ! (
form.elements['filter[AutoArchive]'].checked
||
form.elements['filter[UpdateDiskSpace]'].checked
||
form.elements['filter[AutoVideo]'].checked
||
form.elements['filter[AutoEmail]'].checked
||
form.elements['filter[AutoMessage]'].checked
||
form.elements['filter[AutoExecute]'].checked
||
form.elements['filter[AutoDelete]'].checked
||
form.elements['filter[AutoCopy]'].checked
||
form.elements['filter[AutoMove]'].checked
) ) {
alert('You have chosen to run this filter in the background but not selected any actions.');
}
}
return true; return true;
} }

View File

@ -1,3 +1,7 @@
<?php
global $scale;
?>
var scale = '<?php echo validJsStr($scale); ?>'; var scale = '<?php echo validJsStr($scale); ?>';
var SCALE_BASE = <?php echo SCALE_BASE ?>; var SCALE_BASE = <?php echo SCALE_BASE ?>;

View File

@ -0,0 +1,83 @@
function thumbnail_onmouseover(event) {
var img = event.target;
img.src = '';
img.src = img.getAttribute('full_img_src');
}
function thumbnail_onmouseout(event) {
var img = event.target;
img.src = '';
img.src = img.getAttribute('img_src');
}
function initThumbAnimation() {
$j('.colThumbnail img').each(function() {
this.addEventListener('mouseover', thumbnail_onmouseover, false);
this.addEventListener('mouseout', thumbnail_onmouseout, false);
});
}
function processClicks(event, field, value, row, $element) {
if ( field == 'FramesScore' ) {
if ( value > 0 ) {
window.location.assign('?view=stats&eid='+row.EventId+'&fid='+row.FramesId);
} else {
alert("No statistics available");
}
} else {
window.location.assign('?view=frame&eid='+row.EventId+'&fid='+row.FramesId);
}
}
function initPage() {
var backBtn = $j('#backBtn');
var table = $j('#framesTable');
// Define the icons used in the bootstrap-table top-right toolbar
var icons = {
paginationSwitchDown: 'fa-caret-square-o-down',
paginationSwitchUp: 'fa-caret-square-o-up',
refresh: 'fa-sync',
toggleOff: 'fa-toggle-off',
toggleOn: 'fa-toggle-on',
columns: 'fa-th-list',
fullscreen: 'fa-arrows-alt',
detailOpen: 'fa-plus',
detailClose: 'fa-minus'
};
// Init the bootstrap-table
table.bootstrapTable('destroy').bootstrapTable({icons: icons});
// Hide these columns on first run when no cookie is saved
if ( !getCookie("zmFramesTable.bs.table.columns") ) {
table.bootstrapTable('hideColumn', 'FrameId');
}
// Disable the back button if there is nothing to go back to
backBtn.prop('disabled', !document.referrer.length);
// Setup the thumbnail animation
initThumbAnimation();
// Some toolbar events break the thumbnail animation, so re-init eventlistener
table.on('all.bs.table', initThumbAnimation);
// Load the associated frame image when the user clicks on a row
table.on('click-cell.bs.table', processClicks);
// Manage the BACK button
document.getElementById("backBtn").addEventListener("click", function onBackClick(evt) {
evt.preventDefault();
window.history.back();
});
// Manage the REFRESH Button
document.getElementById("refreshBtn").addEventListener("click", function onRefreshClick(evt) {
evt.preventDefault();
window.location.reload(true);
});
}
$j(document).ready(function() {
initPage();
});

View File

@ -815,10 +815,10 @@ switch ( $tab ) {
<td><?php echo translate('DeviceFormat') ?></td> <td><?php echo translate('DeviceFormat') ?></td>
<td><?php echo htmlSelect('newMonitor[Format]', $v4l2DeviceFormats, $monitor->Format()); ?></td> <td><?php echo htmlSelect('newMonitor[Format]', $v4l2DeviceFormats, $monitor->Format()); ?></td>
</tr> </tr>
<tr> <tr>
<td><?php echo translate('CapturePalette') ?></td> <td><?php echo translate('CapturePalette') ?></td>
<td><?php echo htmlSelect('newMonitor[Palette]', $v4l2LocalPalettes, $monitor->Palette()); ?></td> <td><?php echo htmlSelect('newMonitor[Palette]', $v4l2LocalPalettes, $monitor->Palette()); ?></td>
</tr> </tr>
<?php <?php
} }
?> ?>

View File

@ -38,7 +38,7 @@ xhtmlHeaders(__FILE__, translate('Privacy'));
<h2><?php echo translate('Privacy') ?></h2> <h2><?php echo translate('Privacy') ?></h2>
<h1>ZoneMinder - <?php echo translate('Privacy') ?></h1> <h1>ZoneMinder - <?php echo translate('Privacy') ?></h1>
</div> </div>
<div id="content"> <div id="content" class="container pt-3">
<form name="contentForm" id="contentForm" method="post" action="?"> <form name="contentForm" id="contentForm" method="post" action="?">
<input type="hidden" name="view" value="privacy"/> <input type="hidden" name="view" value="privacy"/>
<input type="hidden" name="action" value="privacy"/> <input type="hidden" name="action" value="privacy"/>
@ -61,9 +61,9 @@ xhtmlHeaders(__FILE__, translate('Privacy'));
<p><?php echo translate('PrivacyTelemetryList') ?></p> <p><?php echo translate('PrivacyTelemetryList') ?></p>
<p><?php echo translate('PrivacyMonitorList') ?></p> <p><?php echo translate('PrivacyMonitorList') ?></p>
<p><?php echo translate('PrivacyConclusionText') ?></p> <p><?php echo translate('PrivacyConclusionText') ?></p>
<p><?php echo htmlSelect('option', $options, ZM_TELEMETRY_DATA); ?></p>
<div id="contentButtons"> <div id="contentButtons" class="pb-3">
<?php echo htmlSelect('option', $options, ZM_TELEMETRY_DATA); ?>
<button type="submit" value="Apply"><?php echo translate('Apply') ?></button> <button type="submit" value="Apply"><?php echo translate('Apply') ?></button>
</div> </div>
</form> </form>