Merge branch 'master' into zma_to_thread

This commit is contained in:
Isaac Connor 2020-12-15 10:51:40 -05:00
commit 0702b64cc4
55 changed files with 617 additions and 10138 deletions

View File

@ -450,6 +450,7 @@ CREATE TABLE `Monitors` (
`Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local',
`Function` enum('None','Monitor','Modect','Record','Mocord','Nodect') NOT NULL default 'Monitor',
`Enabled` tinyint(3) unsigned NOT NULL default '1',
`DecodingEnabled` tinyint(3) unsigned NOT NULL default '1',
`LinkedMonitors` varchar(255),
`Triggers` set('X10') NOT NULL default '',
`ONVIF_URL` VARCHAR(255) NOT NULL DEFAULT '',

12
db/zm_update-1.35.16.sql Normal file
View File

@ -0,0 +1,12 @@
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Monitors'
AND column_name = 'DecodingEnabled'
) > 0,
"SELECT 'Column DecodingEnabled already exists in Monitors'",
"ALTER TABLE Monitors ADD `DecodingEnabled` tinyint(3) unsigned NOT NULL default '1' AFTER `Enabled`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

View File

@ -28,7 +28,7 @@
%global _hardened_build 1
Name: zoneminder
Version: 1.35.15
Version: 1.35.16
Release: 1%{?dist}
Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons

View File

@ -32,6 +32,7 @@ require ZoneMinder::Base;
require ZoneMinder::Object;
require ZoneMinder::Storage;
require ZoneMinder::Frame;
require ZoneMinder::Monitor;
require Date::Manip;
require File::Find;
require File::Path;
@ -915,6 +916,15 @@ sub canEdit {
return 0;
} # end sub canEdit
sub Monitor {
my $self = shift;
$$self{Monitor} = shift if @_;
if ( !$$self{Monitor} ) {
$$self{Monitor} = new ZoneMinder::Monitor($$self{MonitorId});
}
return $$self{Monitor};
}
1;
__END__

View File

@ -91,6 +91,9 @@ while( 1 ) {
}
} # end if ZM_LOG_DATABASE_LIMIT
# Delete any sessions that are more ethan a week old. Limiting to 100 because mysql sucks
zmDbDo('DELETE FROM Sessions WHERE access < ? LIMIT 100', time - (60*60*24*7));
sleep($Config{ZM_STATS_UPDATE_INTERVAL});
} # end while (1)

View File

@ -71,8 +71,8 @@
// This is the official SQL (and ordering of the fields) to load a Monitor.
// It will be used whereever a Monitor dbrow is needed. WHERE conditions can be appended
std::string load_monitor_sql =
"SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Function`+0, `Enabled`, `LinkedMonitors`, "
"`AnalysisFPSLimit`, `AnalysisUpdateDelay`, `MaxFPS`, `AlarmMaxFPS`,"
"SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Function`+0, `Enabled`, `DecodingEnabled`, "
"`LinkedMonitors`, `AnalysisFPSLimit`, `AnalysisUpdateDelay`, `MaxFPS`, `AlarmMaxFPS`,"
"`Device`, `Channel`, `Format`, `V4LMultiBuffer`, `V4LCapturesPerFrame`, " // V4L Settings
"`Protocol`, `Method`, `Options`, `User`, `Pass`, `Host`, `Port`, `Path`, `Width`, `Height`, `Colours`, `Palette`, `Orientation`+0, `Deinterlacing`, "
"`DecoderHWAccelName`, `DecoderHWAccelDevice`, `RTSPDescribe`, "
@ -596,6 +596,18 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
if ( mkdir(monitor_dir.c_str(), 0755) && ( errno != EEXIST ) ) {
Error("Can't mkdir %s: %s", monitor_dir.c_str(), strerror(errno));
}
decoding_enabled = !(
( function == RECORD or function == NODECT )
and
( savejpegs == 0 )
and
( videowriter == H264PASSTHROUGH )
and
!decoding_enabled
);
Debug(1, "Decoding enabled: %d", decoding_enabled);
} else if ( purpose == ANALYSIS ) {
while (
( !(this->connect() and shared_data->valid) )

View File

@ -243,6 +243,7 @@ protected:
CameraType type;
Function function; // What the monitor is doing
bool enabled; // Whether the monitor is enabled or asleep
bool decoding_enabled; // Whether the monitor will decode h264/h265 packets
std::string protocol;
std::string method;
@ -424,6 +425,9 @@ public:
return false;
return enabled;
}
inline bool DecodingEnabled() const {
return decoding_enabled;
}
inline const char *EventPrefix() const { return event_prefix; }
inline bool Ready() const {
if ( function <= MONITOR ) {

View File

@ -1 +1 @@
1.35.15
1.35.16

View File

@ -6,7 +6,7 @@ $data = array();
// INITIALIZE AND CHECK SANITY
//
if ( !canEdit('Events') ) $message = 'Insufficient permissions for user '.$user['Username'];
if ( !canView('Events') ) $message = 'Insufficient permissions for user '.$user['Username'];
if ( empty($_REQUEST['task']) ) {
$message = 'Must specify a task';
@ -74,10 +74,22 @@ if ( isset($_REQUEST['limit']) ) {
switch ( $task ) {
case 'archive' :
foreach ( $eids as $eid ) archiveRequest($task, $eid);
break;
case 'unarchive' :
# The idea is that anyone can archive, but only people with Event Edit permission can unarchive..
if ( !canEdit('Events') ) {
ajaxError('Insufficient permissions for user '.$user['Username']);
return;
}
foreach ( $eids as $eid ) archiveRequest($task, $eid);
break;
case 'delete' :
if ( !canEdit('Events') ) {
ajaxError('Insufficient permissions for user '.$user['Username']);
return;
}
foreach ( $eids as $eid ) $data[] = deleteRequest($eid);
break;
case 'query' :
@ -217,12 +229,12 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
$event = new ZM\Event($row);
$scale = intval(5*100*ZM_WEB_LIST_THUMB_WIDTH / $event->Width());
$imgSrc = $event->getThumbnailSrc(array(),'&amp;');
$imgSrc = $event->getThumbnailSrc(array(), '&amp;');
$streamSrc = $event->getStreamSrc(array(
'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single', 'rate'=>'400'), '&amp;');
// Modify the row data as needed
$row['imgHtml'] = '<img id="thumbnail' .$event->Id(). '" src="' .$imgSrc. '" alt="' .validHtmlStr('Event ' .$event->Id()). '" style="width:' .validInt($event->ThumbnailWidth()). 'px;height:' .validInt($event->ThumbnailHeight()).'px;" stream_src="' .$streamSrc. '" still_src="' .$imgSrc. '"/>';
$row['imgHtml'] = '<img id="thumbnail' .$event->Id(). '" src="' .$imgSrc. '" alt="Event '.$event->Id().'" width="' .validInt($event->ThumbnailWidth()). '" height="' .validInt($event->ThumbnailHeight()).'" stream_src="' .$streamSrc. '" still_src="' .$imgSrc. '"/>';
$row['Name'] = validHtmlStr($row['Name']);
$row['Archived'] = $row['Archived'] ? translate('Yes') : translate('No');
$row['Emailed'] = $row['Emailed'] ? translate('Yes') : translate('No');

View File

@ -161,15 +161,30 @@ function queryRequest($eid, $search, $advsearch, $sort, $offset, $order, $limit)
$returned_rows = array();
foreach ( array_slice($filtered_rows, $offset, $limit) as $row ) {
if ( ZM_WEB_LIST_THUMBS ) {
# Build the path to the potential analysis image
$analImage = sprintf('%0'.ZM_EVENT_IMAGE_DIGITS.'d-analyse.jpg', $row['FrameId']);
$analPath = $Event->Path().'/'.$analImage;
$alarmFrame = $row['Type'] == 'Alarm';
$hasAnalImage = $alarmFrame && file_exists($analPath) && filesize($analPath);
# Our base img source component, which we will add on to
$base_img_src = '?view=image&amp;fid=' .$row['Id'];
# if an analysis images exists, use it as the thumbnail
if ( $hasAnalImage ) $base_img_src .= '&amp;show=analyse';
# Build the subcomponents needed for the image source
$ratio_factor = $Monitor->ViewHeight() / $Monitor->ViewWidth();
$thmb_width = ZM_WEB_LIST_THUMB_WIDTH ? 'width='.ZM_WEB_LIST_THUMB_WIDTH : '';
$thmb_height = 'height="'.( ZM_WEB_LIST_THUMB_HEIGHT ? ZM_WEB_LIST_THUMB_HEIGHT : ZM_WEB_LIST_THUMB_WIDTH*$ratio_factor ) .'"';
$thmb_fn = 'filename=' .$Event->MonitorId(). '_' .$row['EventId']. '_' .$row['FrameId']. '.jpg';
# Assemble the scaled and unscaled image source image source components
$img_src = join('&amp;', array_filter(array($base_img_src, $thmb_width, $thmb_height, $thmb_fn)));
$full_img_src = join('&amp;', array_filter(array($base_img_src, $thmb_fn)));
$frame_src = '?view=frame&amp;eid=' .$row['EventId']. '&amp;fid=' .$row['FrameId'];
# finally, we assemble the the entire thumbnail img src structure, whew
$row['Thumbnail'] = '<img src="' .$img_src. '" '.$thmb_width. ' ' .$thmb_height. 'img_src="' .$img_src. '" full_img_src="' .$full_img_src. '">';
}
$returned_rows[] = $row;

View File

@ -37,11 +37,39 @@ if ( !canEdit('Monitors') ) return;
</button>
</div>
<div class="modal-body">
<p>
<div class="form-group" id="FunctionFunction">
<label for="newFunction"><?php echo translate('Function') ?></label>
<?php echo htmlSelect('newFunction', ZM\getMonitorFunctionTypes(), null, array('id'=>'newFunction')); ?>
<label for="newEnabled"><?php echo translate('Enabled') ?></label>
<div id="function_help">
<?php
foreach ( ZM\getMonitorFunctionTypes() as $fn => $translated ) {
if ( isset($OLANG['FUNCTION_'.strtoupper($fn)]) ) {
echo '<div class="form-text" id="'.$fn.'Help">'.$OLANG['FUNCTION_'.strtoupper($fn)]['Help'].'</div>';
}
}
?>
</div>
</div>
<div class="form-group" id="FunctionAnalysisEnabled">
<label for="newEnabled"><?php echo translate('Analysis Enabled') ?></label>
<input type="checkbox" name="newEnabled" id="newEnabled" value="1"/>
</p>
<?php
if ( isset($OLANG['FUNCTION_ANALYSIS_ENABLED']) ) {
echo '<div class="form-text">'.$OLANG['FUNCTION_ANALYSIS_ENABLED']['Help'].'</div>';
}
?>
</div>
<div class="form-group" id="FunctionDecodingEnabled">
<label for="newDecodingEnabled"><?php echo translate('Decoding Enabled') ?></label>
<input type="checkbox" name="newDecodingEnabled" id="newDecodingEnabled" value="1"/>
<?php
if ( isset($OLANG['FUNCTION_DECODING_ENABLED']) ) {
echo '<div class="form-text">'.$OLANG['FUNCTION_DECODING_ENABLED']['Help'].'</div>';
}
?>
</div>
</div>
<div class="modal-footer">
<button type="button" class="funcSaveBtn btn btn-primary"><?php echo translate('Save') ?></button>

View File

@ -6,6 +6,7 @@ if ( empty($_REQUEST['fid']) ) ajaxError('Frame Id Not Provided');
$eid = $_REQUEST['eid'];
$fid = $_REQUEST['fid'];
$row = ( isset($_REQUEST['row']) ) ? $_REQUEST['row'] : '';
$raw = isset($_REQUEST['raw']);
$data = array();
// Not sure if this is required
@ -16,8 +17,28 @@ if ( ZM_OPT_USE_AUTH && (ZM_AUTH_RELAY == 'hashed') ) {
}
}
$data['html'] = getStatsTableHTML($eid, $fid, $row);
$data['id'] = '#contentStatsTable' .$row;
if ( $raw ) {
$sql = 'SELECT S.*,E.*,Z.Name AS ZoneName,Z.Units,Z.Area,M.Name AS MonitorName FROM Stats AS S LEFT JOIN Events AS E ON S.EventId = E.Id LEFT JOIN Zones AS Z ON S.ZoneId = Z.Id LEFT JOIN Monitors AS M ON E.MonitorId = M.Id WHERE S.EventId = ? AND S.FrameId = ? ORDER BY S.ZoneId';
$stat = dbFetchOne( $sql, NULL, array( $eid, $fid ) );
if ( $stat ) {
$stat['ZoneName'] = validHtmlStr($stat['ZoneName']);
$stat['PixelDiff'] = validHtmlStr($stat['PixelDiff']);
$stat['AlarmPixels'] = sprintf( "%d (%d%%)", $stat['AlarmPixels'], (100*$stat['AlarmPixels']/$stat['Area']) );
$stat['FilterPixels'] = sprintf( "%d (%d%%)", $stat['FilterPixels'], (100*$stat['FilterPixels']/$stat['Area']) );
$stat['BlobPixels'] = sprintf( "%d (%d%%)", $stat['BlobPixels'], (100*$stat['BlobPixels']/$stat['Area']) );
$stat['Blobs'] = validHtmlStr($stat['Blobs']);
if ( $stat['Blobs'] > 1 ) {
$stat['BlobSizes'] = sprintf( "%d-%d (%d%%-%d%%)", $stat['MinBlobSize'], $stat['MaxBlobSize'], (100*$stat['MinBlobSize']/$stat['Area']), (100*$stat['MaxBlobSize']/$stat['Area']) );
} else {
$stat['BlobSizes'] = sprintf( "%d (%d%%)", $stat['MinBlobSize'], 100*$stat['MinBlobSize']/$stat['Area'] );
}
$stat['AlarmLimits'] = validHtmlStr($stat['MinX'].",".$stat['MinY']."-".$stat['MaxX'].",".$stat['MaxY']);
}
$data['raw'] = $stat;
} else {
$data['html'] = getStatsTableHTML($eid, $fid, $row);
$data['id'] = '#contentStatsTable' .$row;
}
ajaxResponse($data);
return;

View File

@ -35,6 +35,7 @@ class Monitor extends ZM_Object {
'Type' => 'Ffmpeg',
'Function' => 'Mocord',
'Enabled' => array('type'=>'boolean','default'=>1),
'DecodingEnabled' => array('type'=>'boolean','default'=>1),
'LinkedMonitors' => array('type'=>'set', 'default'=>null),
'Triggers' => array('type'=>'set','default'=>''),
'ONVIF_URL' => '',

View File

@ -250,8 +250,8 @@ class ZM_Object {
} else if ( property_exists($this, $field) ) {
$type = (array_key_exists($field, $this->defaults) && is_array($this->defaults[$field])) ? $this->defaults[$field]['type'] : 'scalar';
if ( $type == 'set' ) {
$old_value = is_array($this->$field) ? $this->$field : explode(',', $this->$field);
$new_value = is_array($value) ? $value : explode(',', $value);
$old_value = is_array($this->$field) ? $this->$field : ($this->$field ? explode(',', $this->$field) : array());
$new_value = is_array($value) ? $value : ($value ? explode(',', $value) : array());
$diff = array_recursive_diff($old_value, $new_value);
if ( count($diff) ) {

View File

@ -55,6 +55,7 @@ if ( $action == 'save' ) {
'Controllable' => 0,
'TrackMotion' => 0,
'Enabled' => 0,
'DecodingEnabled' => 0,
'Exif' => 0,
'RTSPDescribe' => 0,
'V4LMultiBuffer' => '',
@ -82,7 +83,7 @@ if ( $action == 'save' ) {
}
}
$changes = $monitor->changes($_REQUEST['newMonitor'], $types);
$changes = $monitor->changes($_REQUEST['newMonitor']);
$restart = false;
if ( count($changes) ) {

View File

@ -105,7 +105,7 @@ function validateUser($username='', $password='') {
function userLogout() {
global $user;
ZM\Info('User "'.$user['Username'].'" logged out');
ZM\Info('User "'.($user?$user['Username']:'no one').'" logged out');
$user = null;// unset only clears the local variable
zm_session_clear();
}
@ -187,7 +187,26 @@ function getAuthUser($auth) {
} // end if $auth == $authHash
} // end foreach hour
} // end foreach user
if ( isset($_SESSION['username']) ) {
# In a multi-server case, we might be logged in as another user and so the auth hash didn't work
$sql = 'SELECT * FROM Users WHERE Enabled = 1 AND Username != ?';
foreach ( dbFetchAll($sql, NULL, $values) as $user ) {
$now = time();
for ( $i = 0; $i < ZM_AUTH_HASH_TTL; $i++, $now -= 3600 ) { // Try for last TTL hours
$time = localtime($now);
$authKey = ZM_AUTH_HASH_SECRET.$user['Username'].$user['Password'].$remoteAddr.$time[2].$time[3].$time[4].$time[5];
$authHash = md5($authKey);
if ( $auth == $authHash ) {
return $user;
} // end if $auth == $authHash
} // end foreach hour
} // end foreach user
} // end if
} // end if using auth hash
ZM\Error("Unable to authenticate user from auth hash '$auth'");
return null;
} // end getAuthUser($auth)

View File

@ -1200,8 +1200,8 @@ function sortHeader($field, $querySep='&amp;') {
'?view='.$view,
'page=1'.(isset($_REQUEST['filter'])?$_REQUEST['filter']['query']:''),
'sort_field='.$field,
'sort_asc='.($_REQUEST['sort_field'] == $field ? !$_REQUEST['sort_asc'] : 0),
'limit='.validInt($_REQUEST['limit']),
'sort_asc='.( ( isset($_REQUEST['sort_field']) and ( $_REQUEST['sort_field'] == $field ) ) ? !$_REQUEST['sort_asc'] : 0),
'limit='.(isset($_REQUEST['limit']) ? validInt($_REQUEST['limit']) : ''),
(isset($_REQUEST['eid']) ? 'eid='.$_REQUEST['eid'] : '' ),
));
}
@ -2149,14 +2149,15 @@ function folder_size($dir) {
} // end function folder_size
function human_filesize($size, $precision = 2) {
$units = array('B','kB','MB','GB','TB','PB','EB','ZB','YB');
$units = array('B ','kB','MB','GB','TB','PB','EB','ZB','YB');
$step = 1024;
$i = 0;
while (($size / $step) > 0.9) {
$size = $size / $step;
$i++;
}
return round($size, $precision).$units[$i];
# The idea is that we can right align this and have the digits columns line up nicely.
return sprintf('%.'.$precision.'f', round($size, $precision)).$units[$i];
}
function csrf_startup() {

View File

@ -1073,6 +1073,70 @@ $OLANG = array(
certainly not what you want! To unlink monitors you can ctrl-click.
'
),
'FUNCTION_NONE' => array(
'Help' => '
In None mode no processes are started. No capturing will occur.
'
),
'FUNCTION_MONITOR' => array(
'Help' => '
In Monitor mode the capture process (zmc) will connect to the camera and stream data.
It will be decoded if necessary and live viewing will be possible.
No motion detection will be performed. This monitor type cannot save video.
'
),
'FUNCTION_MODECT' => array(
'Help' => '
In Modect mode the capture process (zmc) will connect to the camera and stream data.
It will be decoded if necessary and live viewing will be possible.
In addition a process (zma) will analyse the video for motion.
When motion is detected, events will be created and video will be stored.
Motion data will be stored in the database for each event.
Events may also be triggered externally (zmtrigger) or by linked monitors.
'
),
'FUNCTION_RECORD' => array(
'Help' => '
In Record mode the capture process (zmc) will connect to the camera and stream data.
It will be decoded if necessary and live viewing will be possible.
In addition a process (zma) will run but will not perform motion detection.
Events will be created at fixed intervals and video will be stored.
'
),
'FUNCTION_MOCORD' => array(
'Help' => '
In Mocord mode the capture process (zmc) will connect to the camera and stream data.
It will be decoded if necessary and live viewing will be possible.
In addition a process (zma) will analyse the video for motion.
Events will be created at fixed intervals or at start and stop of motion.
Video will always be stored to disk and events will have the motion data stored in the database.
Events may also be triggered externally (zmtrigger) or by linked monitors.
'
),
'FUNCTION_NODECT' => array(
'Help' => '
In Nodect mode the capture process (zmc) will connect to the camera and stream data.
It will be decoded if necessary and live viewing will be possible.
In addition a process (zma) will run and will check any linked cameras for their alarm status.
When linked cameras or an external trigger (zmtrigger) are alarmed, events will be created
and video will be stored. No other motion detection will occur.
'
),
'FUNCTION_ANALYSIS_ENABLED' => array(
'Help' => '
When in Modect, Mocord, Nodect or RECORD mode the analysis process can be turned on/off.
This setting sets the default state when the process starts up.
It can then be turned on/off through external triggers zmtrigger zmu or the web ui.
When not enabled no motion detection or linked monitor checking will be performed and
no events will be created. The zma process will still be running waiting to be enabled.
'
),
'FUNCTION_DECODING_ENABLED' => array(
'Help' => '
When in Record or Nodect mode and using H264Passthrough with no jpegs being saved, we can
optionally choose to not decode the H264/H265 packets. This will drastically reduce cpu use
but will make live view unavailable for this monitor.'
),
// 'LANG_DEFAULT' => array(
// 'Prompt' => "This is a new prompt for this option",

View File

@ -741,3 +741,6 @@ a.flip {
float: right;
margin-right: -20px;
}
#content table.major .colDiskSpace {
text-align: right;
}

View File

@ -99,3 +99,6 @@
.StatusFilter select {
min-width: 130px;
}
#FunctionFunction {
margin-bottom: 2rem;
}

View File

@ -42,3 +42,6 @@ input[name="newMonitor[Height]"] {
select.chosen {
width: 100%;
}
tr td:first-child {
min-width: 300px;
}

View File

@ -871,8 +871,8 @@ function xhtmlFooter() {
<script src="tools/mootools/mootools-more.js"></script>
<script src="js/mootools.ext.js"></script>
<?php } ?>
<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.min.js"></script>
<script src="skins/<?php echo $skin; ?>/js/jquery-ui-1.12.1/jquery-ui.min.js"></script>
<script src="skins/<?php echo $skin; ?>/js/bootstrap.min.js"></script>
<?php echo output_script_if_exists(array(
'js/bootstrap-table.min.js',

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
jquery-2.2.4.js

View File

@ -1 +0,0 @@
https://code.jquery.com/jquery-2.2.4.js

1
web/skins/classic/js/jquery.min.js vendored Symbolic link
View File

@ -0,0 +1 @@
jquery-3.5.1.min.js

View File

@ -0,0 +1 @@
https://code.jquery.com/jquery-3.5.1.min.js

View File

@ -241,7 +241,7 @@ if ( currentView != 'none' && currentView != 'login' ) {
$j(document).ready(function() {
// Load the Logout and State modals into the dom
$j('#logoutButton').click(clickLogout);
if ( canEditSystem ) $j('#stateModalBtn').click(getStateModal);
if ( canEdit.System ) $j('#stateModalBtn').click(getStateModal);
// Trigger autorefresh of the widget bar stats on the navbar
if ( $j('.navbar').length ) {
@ -894,8 +894,9 @@ function thumbnail_onmouseover(event) {
timeout = setTimeout(function() {
var img = event.target;
var imgClass = ( currentView == 'console' ) ? 'zoom-console' : 'zoom';
var imgAttr = ( currentView == 'frames' ) ? 'full_img_src' : 'stream_src';
img.src = '';
img.src = img.getAttribute('stream_src');
img.src = img.getAttribute(imgAttr);
img.addClass(imgClass);
}, 350);
}
@ -904,8 +905,9 @@ function thumbnail_onmouseout(event) {
clearTimeout(timeout);
var img = event.target;
var imgClass = ( currentView == 'console' ) ? 'zoom-console' : 'zoom';
var imgAttr = ( currentView == 'frames' ) ? 'img_src' : 'still_src';
img.src = '';
img.src = img.getAttribute('still_src');
img.src = img.getAttribute(imgAttr);
img.removeClass(imgClass);
}

View File

@ -40,14 +40,17 @@ var thisUrl = '<?php echo ZM_BASE_URL.preg_replace('/\.php.*$/i', '.php', $_SERV
var skinPath = '<?php echo ZM_SKIN_PATH ?>';
var serverId = '<?php echo defined('ZM_SERVER_ID') ? ZM_SERVER_ID : '' ?>';
var canEditSystem = <?php echo canEdit('System')?'true':'false' ?>;
var canViewSystem = <?php echo canView('System')?'true':'false' ?>;
var canEditEvents = <?php echo canEdit('Events')?'true':'false' ?>;
var canViewEvents = <?php echo canView('Events')?'true':'false' ?>;
var canEditMonitors = <?php echo canEdit('Monitors')?'true':'false' ?>;
var canViewMonitors = <?php echo canView('Monitors')?'true':'false' ?>;
var canEditGroups = <?php echo canEdit('Groups')?'true':'false' ?>;
var canView = {};
var canEdit = {};
<?php
$perms = array("Stream", "Events", "Control", "Monitors", "Groups", "System", "Devices");
foreach ( $perms as $perm ) {
?>
canView["<?php echo $perm ?>"] = <?php echo canView($perm)?'true':'false' ?>;
canEdit["<?php echo $perm ?>"] = <?php echo canEdit($perm)?'true':'false' ?>;
<?php
}
?>
var ANIMATE_THUMBS = <?php echo ZM_WEB_ANIMATE_THUMBS?'true':'false' ?>;

View File

@ -135,8 +135,6 @@ if ( !$Event->Id() ) {
} else {
if ( !file_exists($Event->Path()) )
echo '<div class="error">Event was not found at '.$Event->Path().'. It is unlikely that playback will be possible.</div>';
$storage = validHtmlStr($Event->Storage()->Name()).( $Event->SecondaryStorageId() ? ', '.validHtmlStr($Event->SecondaryStorage()->Name()) : '' );
?>
<!-- BEGIN HEADER -->
@ -214,64 +212,7 @@ if ( canEdit('Events') ) {
<div class="">
<!-- VIDEO STATISTICS TABLE -->
<table id="eventStatsTable" class="table-sm table-borderless">
<tbody>
<tr>
<th class="text-right"><?php echo translate('EventId') ?></th>
<td id="dataEventId"><?php echo $Event->Id() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('EventName') ?></th>
<td id="dataEventName"><?php echo $Event->Name() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrMonitorId') ?></th>
<td id="dataMonitorId"><?php echo $Monitor->Id() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrMonitorName') ?></th>
<td id="dataMonitorName"><?php echo validHtmlStr($Monitor->Name()) ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('Cause') ?></th>
<td id="dataCause"><?php echo validHtmlStr($Event->Cause()) ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrStartTime') ?></th>
<td id="dataStartTime"><?php echo strftime(STRF_FMT_DATETIME_SHORT, strtotime($Event->StartDateTime())) ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('Duration') ?></th>
<td id="dataDuration"><?php echo $Event->Length().'s' ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrFrames') ?></th>
<td id="dataFrames"><?php echo $Event->Frames() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrAlarmFrames') ?></th>
<td id="dataAlarmFrames"><?php echo $Event->AlarmFrames() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrTotalScore') ?></th>
<td id="dataTotalScore"><?php echo $Event->TotScore() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrAvgScore') ?></th>
<td id="dataAvgScore"><?php echo $Event->AvgScore() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrMaxScore') ?></th>
<td id="dataMaxScore"><?php echo $Event->MaxScore() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('DiskSpace') ?></th>
<td id="dataDiskSpace"><?php echo human_filesize($Event->DiskSpace(null)) ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('Storage') ?></th>
<td id="dataStorage"><?php echo $storage?></td>
</tr>
</tbody>
<!-- EVENT STATISTICS POPULATED BY JAVASCRIPT -->
</table>
</div>
<div class="">

View File

@ -143,21 +143,21 @@ $event_count = 0;
while ( $event_row = dbFetchNext($results) ) {
$event = new ZM\Event($event_row);
$scale = max(reScale(SCALE_BASE, $event->Monitor()->DefaultScale(), ZM_WEB_DEFAULT_SCALE), SCALE_BASE);
$event_link = '?view=event&amp;eid='.$event->Id().$filterQuery.$sortQuery.'&amp;page=1';
?>
<tr<?php echo $event->Archived() ? ' class="archived"' : '' ?>>
<td class="colId">
<input type="hidden" name="eids[]" value="<?php echo $event->Id()?>"/>
<a href="?view=event&amp;eid=<?php echo $event->Id().$filterQuery.$sortQuery ?>&amp;page=1"><?php echo $event->Id().($event->Archived()?'*':'') ?></a>
<a href="<?php echo $event_link ?>"><?php echo $event->Id().($event->Archived()?'*':'') ?></a>
</td>
<td class="colName"><a href="?view=event&amp;eid=<?php echo $event->Id().$filterQuery.$sortQuery ?>&amp;page=1"><?php echo validHtmlStr($event->Name()).($event->Archived()?'*':'') ?></a></td>
<td class="colMonitorName"><?php echo makeLink( '?view=monitor&amp;mid='.$event->MonitorId(), $event->MonitorName(), canEdit( 'Monitors' ) ) ?></td>
<td class="colCause"><?php echo makeLink( '#', validHtmlStr($event->Cause()), canEdit( 'Events' ), 'title="' .htmlspecialchars($event->Notes()). '" class="eDetailLink" data-eid=' .$event->Id(). '"') ?></td>
<td class="colName"><a href="<?php echo $event_link ?>"><?php echo validHtmlStr($event->Name()).($event->Archived()?'*':'') ?></a></td>
<td class="colMonitorName"><?php echo makeLink('?view=monitor&amp;mid='.$event->MonitorId(), $event->MonitorName(), canEdit('Monitors')) ?></td>
<td class="colCause"><?php echo makeLink($event_link, validHtmlStr($event->Cause()), canView('Events'), 'title="' .htmlspecialchars($event->Notes()). '" class="eDetailLink" data-eid="'.$event->Id().'"') ?></td>
<td class="colTime"><?php echo strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->StartDateTime())) .
( $event->EndDateTime() ? ' until ' . strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->EndDateTime()) ) : '' ) ?>
</td>
<td class="colDuration"><?php echo gmdate("H:i:s", $event->Length() ) ?></td>
<td class="colFrames"><?php echo makeLink( '?view=frames&amp;eid='.$event->Id(), $event->Frames() ) ?></td>
<td class="colAlarmFrames"><?php echo makeLink( '?view=frames&amp;eid='.$event->Id(), $event->AlarmFrames() ) ?></td>
( $event->EndDateTime() ? ' until ' . strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->EndDateTime())) : '' ) ?></td>
<td class="colDuration"><?php echo gmdate('H:i:s', $event->Length()) ?></td>
<td class="colFrames"><?php echo makeLink('?view=frames&amp;eid='.$event->Id(), $event->Frames()) ?></td>
<td class="colAlarmFrames"><?php echo makeLink('?view=frames&amp;eid='.$event->Id(), $event->AlarmFrames()) ?></td>
<td class="colTotScore"><?php echo $event->TotScore() ?></td>
<td class="colAvgScore"><?php echo $event->AvgScore() ?></td>
<td class="colMaxScore"><?php echo $event->MaxScore() ?></td>
@ -168,9 +168,7 @@ while ( $event_row = dbFetchNext($results) ) {
echo '<td class="colDiskSpace">'.human_filesize($event->DiskSpace()).'</td>';
}
unset($event);
echo '
</tr>
';
echo PHP_EOL.'</tr>'.PHP_EOL;
} # end foreach event
?>
</tbody>
@ -179,9 +177,7 @@ while ( $event_row = dbFetchNext($results) ) {
<td colspan="11"><?php echo $event_count ?> events</td>
<?php
if ( ZM_WEB_EVENT_DISK_SPACE ) {
?>
<td class="colDiskSpace"><?php echo human_filesize($disk_space_total);?></td>
<?php
echo '<td class="colDiskSpace">'.human_filesize($disk_space_total).'</td>'.PHP_EOL;
}
?>
</tr>

View File

@ -49,7 +49,7 @@ $prevFid = $fid-1;
$nextFid = $fid+1;
$lastFid = $maxFid;
$alarmFrame = $Frame->Type() == 'Alarm';
$alarmFrame = ( $Frame->Type() == 'Alarm' ) ? 1 : 0;
if ( isset($_REQUEST['scale']) ) {
$scale = validNum($_REQUEST['scale']);
@ -103,7 +103,14 @@ xhtmlHeaders(__FILE__, translate('Frame').' - '.$Event->Id().' - '.$Frame->Frame
</form>
</div>
<div id="content">
<div id="content" class="d-flex flex-row justify-content-center">
<table id="frameStatsTable" class="table-sm table-borderless pr-3">
<!-- FRAME STATISTICS POPULATED BY AJAX -->
</table>
<div>
<p id="image">
<?php if ( $imageData['hasAnalImage'] ) {
echo sprintf('<a href="?view=frame&amp;eid=%d&amp;fid=%d&scale=%d&amp;show=%s">', $Event->Id(), $Frame->FrameId(), $scale, ( $show=='anal'?'capt':'anal' ) );
@ -152,6 +159,7 @@ if ( file_exists($rImagePath) ) {
class="<?php echo $imageData['imageClass'] ?>"
/>
</p>
</div>
<?php } ?>
</div>
</div>

View File

@ -112,7 +112,7 @@ function reloadWindow() {
function manageFunctionModal(evt) {
evt.preventDefault();
if ( !canEditEvents ) {
if ( !canEdit.Events ) {
enoperm();
return;
}
@ -151,8 +151,25 @@ function manageFunctionModal(evt) {
console.error("Unable to find form with id function_form");
return;
}
function_form.elements['newFunction'].onchange=function() {
$j('#function_help div').hide();
$j('#'+this.value+'Help').show();
if ( this.value == 'Monitor' || this.value == 'None' ) {
$j('#FunctionAnalysisEnabled').hide();
} else {
$j('#FunctionAnalysisEnabled').show();
}
if ( this.value == 'Record' || this.value == 'Nodect' ) {
$j('#FunctionDecodingEnabled').show();
} else {
$j('#FunctionDecodingEnabled').hide();
}
};
function_form.elements['newFunction'].value = monitor.Function;
function_form.elements['newFunction'].onchange();
function_form.elements['newEnabled'].checked = monitor.Enabled == '1';
function_form.elements['newDecodingEnabled'].checked = monitor.DecodingEnabled == '1';
function_form.elements['mid'].value = mid;
document.getElementById('function_monitor_name').innerHTML = monitor.Name;

View File

@ -24,7 +24,8 @@ var monitors = new Array();
'Url': '<?php echo $monitor->UrlToIndex( ZM_MIN_STREAMING_PORT ? ($monitor->Id() + ZM_MIN_STREAMING_PORT) : '') ?>',
'Type': '<?php echo $monitor->Type() ?>',
'Function': '<?php echo $monitor->Function() ?>',
'Enabled': '<?php echo $monitor->Enabled() ?>'
'Enabled': '<?php echo $monitor->Enabled() ?>',
'DecodingEnabled': '<?php echo $monitor->DecodingEnabled() ?>'
};
<?php
}

View File

@ -43,7 +43,7 @@ function getDelConfirmModal(key) {
// Manage the DELETE CONFIRMATION modal button
function manageDelConfirmModalBtns() {
document.getElementById("delConfirmBtn").addEventListener("click", function onDelConfirmClick(evt) {
if ( ! canEditControl ) {
if ( ! canEdit.Control ) {
enoperm();
return;
}
@ -67,9 +67,9 @@ function initPage() {
function() {
selections = table.bootstrapTable('getSelections');
addNewBtn.prop('disabled', (selections.length || !canEditControl));
editBtn.prop('disabled', !((selections.length == 1) && canEditControl));
deleteBtn.prop('disabled', !(selections.length && canEditControl));
addNewBtn.prop('disabled', (selections.length || !canEdit.Control));
editBtn.prop('disabled', !((selections.length == 1) && canEdit.Control));
deleteBtn.prop('disabled', !(selections.length && canEdit.Control));
});
// Init the bootstrap-table
@ -92,7 +92,7 @@ function initPage() {
// Manage the DELETE button
document.getElementById("deleteBtn").addEventListener("click", function onDeleteClick(evt) {
if ( ! canEditControl ) {
if ( ! canEdit.Control ) {
enoperm();
return;
}

View File

@ -1 +0,0 @@
var canEditControl = <?php echo canEdit('Control')?'true':'false' ?>;

View File

@ -42,7 +42,7 @@ function getDelConfirmModal(key) {
// Manage the DELETE CONFIRMATION modal button
function manageDelConfirmModalBtns() {
document.getElementById("delConfirmBtn").addEventListener("click", function onDelConfirmClick(evt) {
if ( ! canEditDevice ) {
if ( ! canEdit.Device ) {
enoperm();
return;
}
@ -91,9 +91,9 @@ function initPage() {
// Init the bootstrap-table
table.bootstrapTable({icons: icons});
if ( canEditDevice ) enableDeviceModal();
if ( canEdit.Device ) enableDeviceModal();
newDeviceBtn.prop('disabled', !canEditDevice);
newDeviceBtn.prop('disabled', !canEdit.Device);
// Manage the BACK button
document.getElementById("backBtn").addEventListener("click", function onBackClick(evt) {
@ -112,7 +112,7 @@ function initPage() {
// Manage the DELETE button
document.getElementById("deleteBtn").addEventListener("click", function onDeleteClick(evt) {
if ( ! canEditDevice ) {
if ( ! canEdit.Device ) {
enoperm();
return;
}
@ -130,7 +130,7 @@ function initPage() {
function() {
selections = table.bootstrapTable('getSelections');
deleteBtn.prop('disabled', !(selections.length && canEditDevice));
deleteBtn.prop('disabled', !(selections.length && canEdit.Device));
});
// Process mouse clicks on the table cells

View File

@ -1 +0,0 @@
var canEditDevice = <?php echo canEdit('Devices') ? 'true' : 'false' ?>;

View File

@ -598,8 +598,8 @@ function getEventResponse(respObj, respText) {
$j('dataStorage').text( eventData.Storage );
// Refresh the status of the archive buttons
archiveBtn.prop('disabled', !(!eventData.Archived && canEditEvents));
unarchiveBtn.prop('disabled', !(eventData.Archived && canEditEvents));
archiveBtn.prop('disabled', !(!eventData.Archived && canEdit.Events));
unarchiveBtn.prop('disabled', !(eventData.Archived && canEdit.Events));
history.replaceState(null, null, '?view=event&eid=' + eventData.Id + filterQuery + sortQuery); //if popup removed, check if this allows forward
// Technically, events can be different sizes, so may need to update the size of the image, but it might be better to have it stay scaled...
@ -1032,20 +1032,10 @@ function handleClick( event ) {
}
}
// Load the Delete Confirmation Modal HTML via Ajax call
function getDelConfirmModal() {
$j.getJSON(thisUrl + '?request=modal&modal=delconfirm')
.done(function(data) {
insertModalHtml('deleteConfirm', data.html);
manageDelConfirmModalBtns();
})
.fail(logAjaxFail);
}
// Manage the DELETE CONFIRMATION modal button
function manageDelConfirmModalBtns() {
document.getElementById("delConfirmBtn").addEventListener("click", function onDelConfirmClick(evt) {
if ( ! canEditEvents ) {
if ( ! canEdit.Events ) {
enoperm();
return;
}
@ -1075,9 +1065,21 @@ function getEvtStatsCookie() {
return stats;
}
function getStat() {
table.empty().append('<tbody>');
$j.each( eventDataStrings, function( key ) {
var th = $j('<th>').addClass('text-right').text(eventDataStrings[key]);
var tdString = ( eventData[key].length ) ? eventData[key] : 'n/a';
var td = $j('<td>').text(tdString);
var row = $j('<tr>').append(th, td);
$j('#eventStatsTable tbody').append(row);
});
}
function initPage() {
// Load the delete confirmation modal into the DOM
getDelConfirmModal();
// Load the event stats
getStat();
var stats = getEvtStatsCookie();
if ( stats != 'on' ) table.toggle(false);
@ -1134,13 +1136,13 @@ function initPage() {
});
// enable or disable buttons based on current selection and user rights
renameBtn.prop('disabled', !canEditEvents);
archiveBtn.prop('disabled', !(!eventData.Archived && canEditEvents));
unarchiveBtn.prop('disabled', !(eventData.Archived && canEditEvents));
editBtn.prop('disabled', !canEditEvents);
exportBtn.prop('disabled', !canViewEvents);
downloadBtn.prop('disabled', !canViewEvents);
deleteBtn.prop('disabled', !canEditEvents);
renameBtn.prop('disabled', !canEdit.Events);
archiveBtn.prop('disabled', !(!eventData.Archived && canEdit.Events));
unarchiveBtn.prop('disabled', !(eventData.Archived && canEdit.Events));
editBtn.prop('disabled', !canEdit.Events);
exportBtn.prop('disabled', !canView.Events);
downloadBtn.prop('disabled', !canView.Events);
deleteBtn.prop('disabled', !canEdit.Events);
// Don't enable the back button if there is no previous zm page to go back to
backBtn.prop('disabled', !document.referrer.length);
@ -1183,7 +1185,7 @@ function initPage() {
// Manage the UNARCHIVE button
document.getElementById("unarchiveBtn").addEventListener("click", function onUnarchiveClick(evt) {
if ( ! canEditEvents ) {
if ( ! canEdit.Events ) {
enoperm();
return;
}
@ -1198,7 +1200,7 @@ function initPage() {
// Manage the EDIT button
document.getElementById("editBtn").addEventListener("click", function onEditClick(evt) {
if ( ! canEditEvents ) {
if ( ! canEdit.Events ) {
enoperm();
return;
}
@ -1253,12 +1255,23 @@ function initPage() {
// Manage the DELETE button
document.getElementById("deleteBtn").addEventListener("click", function onDeleteClick(evt) {
if ( ! canEditEvents ) {
if ( ! canEdit.Events ) {
enoperm();
return;
}
evt.preventDefault();
if ( ! $j('#deleteConfirm').length ) {
// Load the delete confirmation modal into the DOM
$j.getJSON(thisUrl + '?request=modal&modal=delconfirm')
.done(function(data) {
insertModalHtml('deleteConfirm', data.html);
manageDelConfirmModalBtns();
$j('#deleteConfirm').modal('show');
})
.fail(logAjaxFail);
return;
}
$j('#deleteConfirm').modal('show');
});
}

View File

@ -42,17 +42,46 @@ var eventData = {
Id: '<?php echo $Event->Id() ?>',
Name: '<?php echo $Event->Name() ?>',
MonitorId: '<?php echo $Event->MonitorId() ?>',
MonitorName: '<?php echo validJsStr($Monitor->Name()) ?>',
Cause: '<?php echo validHtmlStr($Event->Cause()) ?>',
Width: '<?php echo $Event->Width() ?>',
Height: '<?php echo $Event->Height() ?>',
Length: '<?php echo $Event->Length() ?>',
StartDateTime: '<?php echo $Event->StartDateTime() ?>',
StartDateTimeFmt: '<?php echo strftime(STRF_FMT_DATETIME_SHORT, strtotime($Event->StartDateTime())) ?>',
EndDateTime: '<?php echo $Event->EndDateTime() ?>',
Frames: '<?php echo $Event->Frames() ?>',
MonitorName: '<?php echo validJsStr($Monitor->Name()) ?>',
AlarmFrames: '<?php echo $Event->AlarmFrames() ?>',
TotScore: '<?php echo $Event->TotScore() ?>',
AvgScore: '<?php echo $Event->AvgScore() ?>',
MaxScore: '<?php echo $Event->MaxScore() ?>',
DiskSpace: '<?php echo human_filesize($Event->DiskSpace(null)) ?>',
Storage: '<?php validHtmlStr($Event->Storage()->Name()).( $Event->SecondaryStorageId() ? ', '.validHtmlStr($Event->SecondaryStorage()->Name()) : '' ) ?>',
Archived: <?php echo $Event->Archived?'true':'false' ?>
Storage: '<?php echo validHtmlStr($Event->Storage()->Name()).( $Event->SecondaryStorageId() ? ', '.validHtmlStr($Event->SecondaryStorage()->Name()) : '' ) ?>',
ArchivedStr: '<?php echo $Event->Archived ? translate('Yes') : translate('No') ?>',
EmailedStr: '<?php echo $Event->Emailed ? translate('Yes') : translate('No') ?>',
Archived: <?php echo $Event->Archived?'true':'false' ?>,
Emailed: <?php echo $Event->Emailed?'true':'false' ?>
};
var eventDataStrings = {
Id: '<?php echo translate('EventId') ?>',
Name: '<?php echo translate('EventName') ?>',
MonitorId: '<?php echo translate('AttrMonitorId') ?>',
MonitorName: '<?php echo translate('AttrMonitorName') ?>',
Cause: '<?php echo translate('Cause') ?>',
StartDateTimeFmt: '<?php echo translate('AttrStartTime') ?>',
Length: '<?php echo translate('Duration') ?>',
Frames: '<?php echo translate('AttrFrames') ?>',
AlarmFrames: '<?php echo translate('AttrAlarmFrames') ?>',
TotScore: '<?php echo translate('AttrTotalScore') ?>',
AvgScore: '<?php echo translate('AttrAvgScore') ?>',
MaxScore: '<?php echo translate('AttrMaxScore') ?>',
DiskSpace: '<?php echo translate('DiskSpace') ?>',
Storage: '<?php echo translate('Storage') ?>',
ArchivedStr: '<?php echo translate('Archived') ?>',
EmailedStr: '<?php echo translate('Emailed') ?>'
};
var monitorUrl = '<?php echo $Event->Storage()->Server()->UrlToIndex(); ?>';
var filterQuery = '<?php echo isset($filterQuery)?validJsStr(htmlspecialchars_decode($filterQuery)):'' ?>';
@ -63,7 +92,6 @@ var rate = '<?php echo $rate ?>'; // really only used when setting up initial pl
var scale = "<?php echo $scale ?>";
var LabelFormat = "<?php echo validJsStr($Monitor->LabelFormat())?>";
var canEditEvents = <?php echo canEdit('Events')?'true':'false' ?>;
var streamTimeout = <?php echo 1000*ZM_WEB_REFRESH_STATUS ?>;
var canStreamNative = <?php echo canStreamNative()?'true':'false' ?>;

View File

@ -58,8 +58,8 @@ function processRows(rows) {
row.Id = '<a href="?view=event&amp;eid=' + eid + filterQuery + sortQuery + '&amp;page=1">' + eid + '</a>';
row.Name = '<a href="?view=event&amp;eid=' + eid + filterQuery + sortQuery + '&amp;page=1">' + row.Name + '</a>'
+ '<br/><div class="small text-nowrap text-muted">' + archived + emailed + '</div>';
if ( canEditMonitors ) row.Monitor = '<a href="?view=monitor&amp;mid=' + mid + '">' + row.Monitor + '</a>';
if ( canEditEvents ) row.Cause = '<a href="#" title="' + row.Notes + '" class="eDetailLink" data-eid="' + eid + '">' + row.Cause + '</a>';
if ( canEdit.Monitors ) row.Monitor = '<a href="?view=monitor&amp;mid=' + mid + '">' + row.Monitor + '</a>';
if ( canEdit.Events ) row.Cause = '<a href="#" title="' + row.Notes + '" class="eDetailLink" data-eid="' + eid + '">' + row.Cause + '</a>';
if ( row.Notes.indexOf('detected:') >= 0 ) {
row.Cause = row.Cause + '<a href="?view=image&amp;eid=' + eid + '&amp;fid=objdetect"><div class="small text-nowrap text-muted"><u>' + row.Notes + '</u></div></a>';
} else if ( row.Notes != 'Forced Web: ' ) {
@ -105,7 +105,7 @@ function getDelConfirmModal() {
// Manage the DELETE CONFIRMATION modal button
function manageDelConfirmModalBtns() {
document.getElementById("delConfirmBtn").addEventListener("click", function onDelConfirmClick(evt) {
if ( ! canEditEvents ) {
if ( ! canEdit.Events ) {
enoperm();
return;
}
@ -163,13 +163,13 @@ function initPage() {
function() {
selections = table.bootstrapTable('getSelections');
viewBtn.prop('disabled', !(selections.length && canViewEvents));
archiveBtn.prop('disabled', !(selections.length && canEditEvents));
unarchiveBtn.prop('disabled', !(getArchivedSelections()) && canEditEvents);
editBtn.prop('disabled', !(selections.length && canEditEvents));
exportBtn.prop('disabled', !(selections.length && canViewEvents));
downloadBtn.prop('disabled', !(selections.length && canViewEvents));
deleteBtn.prop('disabled', !(selections.length && canEditEvents));
viewBtn.prop('disabled', !(selections.length && canView.Events));
archiveBtn.prop('disabled', !(selections.length && canEdit.Events));
unarchiveBtn.prop('disabled', !(getArchivedSelections()) && canEdit.Events);
editBtn.prop('disabled', !(selections.length && canEdit.Events));
exportBtn.prop('disabled', !(selections.length && canView.Events));
downloadBtn.prop('disabled', !(selections.length && canView.Events));
deleteBtn.prop('disabled', !(selections.length && canEdit.Events));
});
// Don't enable the back button if there is no previous zm page to go back to
@ -228,7 +228,7 @@ function initPage() {
// Manage the UNARCHIVE button
document.getElementById("unarchiveBtn").addEventListener("click", function onUnarchiveClick(evt) {
if ( ! canEditEvents ) {
if ( ! canEdit.Events ) {
enoperm();
return;
}
@ -246,7 +246,7 @@ function initPage() {
// Manage the EDIT button
document.getElementById("editBtn").addEventListener("click", function onEditClick(evt) {
if ( ! canEditEvents ) {
if ( ! canEdit.Events ) {
enoperm();
return;
}
@ -292,7 +292,7 @@ function initPage() {
// Manage the DELETE button
document.getElementById("deleteBtn").addEventListener("click", function onDeleteClick(evt) {
if ( ! canEditEvents ) {
if ( ! canEdit.Events ) {
enoperm();
return;
}

View File

@ -64,12 +64,10 @@ function exportResponse(respObj, respText) {
}
function exportEvents( ) {
var parms = 'view=event&request=event&action=export';
parms += '&'+$('contentForm').toQueryString();
var query = new Request.JSON( {
url: thisUrl,
url: '?view=event&request=event&action=export',
method: 'post',
data: parms,
data: $('contentForm').toQueryString(),
onSuccess: exportResponse
} );
query.send();

View File

@ -40,6 +40,24 @@ document.addEventListener('DOMContentLoaded', function onDCL() {
document.getElementById('scaleControl').addEventListener('change', changeScale);
});
function getStat(params) {
$j.getJSON(thisUrl + '?view=request&request=stats&raw=true', params)
.done(function(data) {
var stat = data.raw;
$j('#frameStatsTable').empty().append('<tbody>');
$j.each( statHeaderStrings, function( key ) {
var th = $j('<th>').addClass('text-right').text(statHeaderStrings[key]);
var tdString = ( stat ) ? stat[key] : 'n/a';
var td = $j('<td>').text(tdString);
var row = $j('<tr>').append(th, td);
$j('#frameStatsTable tbody').append(row);
});
})
.fail(logAjaxFail);
}
function initPage() {
var backBtn = $j('#backBtn');
@ -65,6 +83,9 @@ function initPage() {
evt.preventDefault();
window.location.href = thisUrl+'?view=stats&eid='+eid+'&fid='+fid;
});
// Load the frame stats
getStat({eid: eid, fid: fid});
}
$j(document).ready(function() {

View File

@ -13,3 +13,15 @@ var eid = <?php echo $eid ?>;
var fid = <?php echo $fid ?>;
var record_event_stats = <?php echo ZM_RECORD_EVENT_STATS ?>;
var alarmFrame = <?php echo $alarmFrame ?>;
var statHeaderStrings = {};
statHeaderStrings.ZoneName = "<?php echo translate('Zone') ?>";
statHeaderStrings.PixelDiff = "<?php echo translate('PixelDiff') ?>";
statHeaderStrings.AlarmPixels = "<?php echo translate('AlarmPx') ?>";
statHeaderStrings.FilterPixels = "<?php echo translate('FilterPx') ?>";
statHeaderStrings.BlobPixels = "<?php echo translate('BlobPx') ?>";
statHeaderStrings.Blobs = "<?php echo translate('Blobs') ?>";
statHeaderStrings.BlobSizes = "<?php echo translate('BlobSizes') ?>";
statHeaderStrings.AlarmLimits = "<?php echo translate('AlarmLimits') ?>";
statHeaderStrings.Score = "<?php echo translate('Score') ?>";

View File

@ -26,27 +26,6 @@ function processRows(rows) {
return rows;
}
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() {
if ( WEB_ANIMATE_THUMBS ) {
$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 == 'Score' ) {
window.location.assign('?view=stats&eid='+row.EventId+'&fid='+row.FrameId);
@ -118,8 +97,7 @@ function initPage() {
var thumb_ndx = $j('#framesTable tr th').filter(function() {
return $j(this).text().trim() == 'Thumbnail';
}).index();
var thmbClass = WEB_ANIMATE_THUMBS ? 'colThumbnail zoom' : 'colThumbnail';
table.find("tr td:nth-child(" + (thumb_ndx+1) + ")").addClass(thmbClass);
table.find("tr td:nth-child(" + (thumb_ndx+1) + ")").addClass('colThumbnail');
});
}

View File

@ -50,7 +50,7 @@ function deleteGroup( element ) {
}
function configureButtons( element ) {
if ( canEditGroups ) {
if ( canEdit.Groups ) {
var form = element.form;
if ( element.checked ) {
form.deleteBtn.disabled = (element.value == 0);
@ -64,7 +64,7 @@ function configModalBtns() {
console.log("No groupForm found");
return;
}
if ( !canEditGroups ) {
if ( !canEdit.Groups ) {
console.log("Cannot edit groups");
form.elements['action'].disabled = disabled;
return;

View File

@ -146,6 +146,24 @@ function initPage() {
el.oninput = window['update_estimated_ram_use'].bind(el);
});
document.querySelectorAll('select[name="newMonitor[Function]"]').forEach(function(el) {
el.onchange = function() {
$j('#function_help div').hide();
$j('#'+this.value+'Help').show();
if ( this.value == 'Monitor' || this.value == 'None' ) {
$j('#FunctionEnabled').hide();
} else {
$j('#FunctionEnabled').show();
}
if ( this.value == 'Record' || this.value == 'Nodect' ) {
$j('#FunctionDecodingEnabled').show();
} else {
$j('#FunctionDecodingEnabled').hide();
}
};
el.onchange();
});
$j('.chosen').chosen();
// Don't enable the back button if there is no previous zm page to go back to

View File

@ -62,11 +62,11 @@ function initPage() {
var NewStorageBtn = $j('#NewStorageBtn');
var NewServerBtn = $j('#NewServerBtn');
if ( canEditSystem ) enableStorageModal();
if ( canEditSystem ) enableServerModal();
if ( canEdit.System ) enableStorageModal();
if ( canEdit.System ) enableServerModal();
NewStorageBtn.prop('disabled', !canEditSystem);
NewServerBtn.prop('disabled', !canEditSystem);
NewStorageBtn.prop('disabled', !canEdit.System);
NewServerBtn.prop('disabled', !canEdit.System);
}
$j(document).ready(function() {

View File

@ -4,4 +4,3 @@ if ( restartWarning ) {
alert( "<?php echo translate('OptionRestartWarning') ?>" );
}
var canEditSystem = <?php echo canEdit('System') ? 'true' : 'false' ?>;

View File

@ -1,3 +1,4 @@
var streamCmdTimer = null;
var streamStatus;
var auth_hash;
var alarmState = STATE_IDLE;
@ -9,23 +10,6 @@ var forceAlmBtn = $j('#forceAlmBtn');
var table = $j('#eventList');
var filterQuery = '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId;
if ( monitorType != 'WebSite' ) {
var streamCmdParms = 'view=request&request=stream&connkey='+connKey;
if ( auth_hash ) {
streamCmdParms += '&auth='+auth_hash;
}
var streamCmdReq = new Request.JSON( {
url: monitorUrl,
method: 'get',
timeout: AJAX_TIMEOUT,
link: 'chain',
onError: getStreamCmdError,
onSuccess: getStreamCmdResponse,
onFailure: getStreamCmdFailure
} );
var streamCmdTimer = null;
}
/*
This is the format of the json object sent by bootstrap-table
@ -194,10 +178,6 @@ function getStreamCmdError(text, error) {
window.location.reload();
}
function getStreamCmdFailure(xhr) {
console.log(xhr);
}
function getStreamCmdResponse(respObj, respText) {
watchdogOk('stream');
if ( streamCmdTimer ) {
@ -267,7 +247,7 @@ function getStreamCmdResponse(respObj, respText) {
setButtonState('zoomOutBtn', 'inactive');
}
if ( canEditMonitors ) {
if ( canEdit.Monitors ) {
if ( streamStatus.enabled ) {
enableAlmBtn.addClass('disabled');
enableAlmBtn.prop('title', disableAlarmsStr);
@ -285,7 +265,7 @@ function getStreamCmdResponse(respObj, respText) {
forceAlmBtn.prop('disabled', true);
}
enableAlmBtn.prop('disabled', false);
} // end if canEditMonitors
} // end if canEdit.Monitors
if ( streamStatus.auth ) {
auth_hash = streamStatus.auth;
@ -296,10 +276,7 @@ function getStreamCmdResponse(respObj, respText) {
var newSrc = oldSrc.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
streamImg.src = newSrc;
}
streamCmdParms = streamCmdParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
statusCmdParms = statusCmdParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
table.bootstrapTable('refresh');
controlParms = controlParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
} // end if have a new auth hash
} // end if respObj.status
} else {
@ -339,7 +316,10 @@ function streamCmdPause( action ) {
setButtonState('fastRevBtn', 'inactive');
}
if ( action ) {
streamCmdReq.send(streamCmdParms+"&command="+CMD_PAUSE);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = CMD_PAUSE;
streamCmdReq(data);
}
}
@ -364,10 +344,21 @@ function streamCmdPlay( action ) {
}
}
if ( action ) {
streamCmdReq.send(streamCmdParms+"&command="+CMD_PLAY);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = CMD_PLAY;
streamCmdReq(data);
}
}
function streamCmdReq(data) {
$j.getJSON(thisUrl + '?view=request&request=stream&connkey='+connKey, data)
.done(getStreamCmdResponse)
.fail(getStreamCmdError);
streamCmdTimer = null;
}
function streamCmdStop( action ) {
setButtonState('pauseBtn', 'inactive');
setButtonState('playBtn', 'unavail');
@ -379,7 +370,10 @@ function streamCmdStop( action ) {
setButtonState('fastRevBtn', 'unavail');
}
if ( action ) {
streamCmdReq.send(streamCmdParms+"&command="+CMD_STOP);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = CMD_STOP;
streamCmdReq(data);
}
setButtonState('stopBtn', 'unavail');
setButtonState('playBtn', 'active');
@ -396,7 +390,10 @@ function streamCmdFastFwd( action ) {
setButtonState('fastRevBtn', 'inactive');
}
if ( action ) {
streamCmdReq.send(streamCmdParms+"&command="+CMD_FASTFWD);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = CMD_FASTFWD;
streamCmdReq(data);
}
}
@ -411,7 +408,10 @@ function streamCmdSlowFwd( action ) {
setButtonState('fastRevBtn', 'inactive');
}
if ( action ) {
streamCmdReq.send(streamCmdParms+"&command="+CMD_SLOWFWD);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = CMD_SLOWFWD;
streamCmdReq(data);
}
setButtonState('pauseBtn', 'active');
if ( monitorStreamReplayBuffer ) {
@ -430,7 +430,10 @@ function streamCmdSlowRev( action ) {
setButtonState('fastRevBtn', 'inactive');
}
if ( action ) {
streamCmdReq.send(streamCmdParms+"&command="+CMD_SLOWREV);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = CMD_SLOWREV;
streamCmdReq(data);
}
setButtonState('pauseBtn', 'active');
if ( monitorStreamReplayBuffer ) {
@ -449,43 +452,51 @@ function streamCmdFastRev( action ) {
setButtonState('fastRevBtn', 'inactive');
}
if ( action ) {
streamCmdReq.send(streamCmdParms+"&command="+CMD_FASTREV);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = CMD_FASTREV;
streamCmdReq(data);
}
}
function streamCmdZoomIn( x, y ) {
streamCmdReq.send(streamCmdParms+"&command="+CMD_ZOOMIN+"&x="+x+"&y="+y);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.x = x;
data.y = y;
data.command = CMD_ZOOMIN;
streamCmdReq(data);
}
function streamCmdZoomOut() {
streamCmdReq.send(streamCmdParms+"&command="+CMD_ZOOMOUT);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = CMD_ZOOMOUT;
streamCmdReq(data);
}
function streamCmdScale( scale ) {
streamCmdReq.send(streamCmdParms+"&command="+CMD_SCALE+"&scale="+scale);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = CMD_SCALE;
data.scale = scale;
streamCmdReq(data);
}
function streamCmdPan( x, y ) {
streamCmdReq.send(streamCmdParms+"&command="+CMD_PAN+"&x="+x+"&y="+y);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.x = x;
data.y = y;
data.command = CMD_PAN;
streamCmdReq(data);
}
function streamCmdQuery() {
streamCmdReq.send(streamCmdParms+"&command="+CMD_QUERY);
}
if ( monitorType != 'WebSite' ) {
var statusCmdParms = "view=request&request=status&entity=monitor&id="+monitorId+"&element[]=Status&element[]=FrameRate";
if ( auth_hash ) {
statusCmdParms += '&auth='+auth_hash;
}
var statusCmdReq = new Request.JSON( {
url: monitorUrl,
method: 'get',
timeout: AJAX_TIMEOUT,
link: 'cancel',
onSuccess: getStatusCmdResponse
} );
var statusCmdTimer = null;
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = CMD_QUERY;
streamCmdReq(data);
}
function getStatusCmdResponse(respObj, respText) {
@ -509,23 +520,23 @@ function getStatusCmdResponse(respObj, respText) {
}
function statusCmdQuery() {
statusCmdReq.send(statusCmdParms);
$j.getJSON(thisUrl + '?view=request&request=status&entity=monitor&element[]=Status&element[]=FrameRate&id='+monitorId)
.done(getStatusCmdResponse)
.fail(logAjaxFail);
streamCmdTimer = null;
}
if ( monitorType != 'WebSite' ) {
var alarmCmdParms = 'view=request&request=alarm&id='+monitorId;
if ( auth_hash ) {
alarmCmdParms += '&auth='+auth_hash;
}
var alarmCmdReq = new Request.JSON( {
url: monitorUrl,
method: 'get',
timeout: AJAX_TIMEOUT,
link: 'cancel',
onSuccess: getAlarmCmdResponse,
onTimeout: streamCmdQuery
} );
var alarmCmdFirst = true;
function alarmCmdReq(data) {
$j.getJSON(thisUrl + '?view=request&request=alarm&id='+monitorId, data)
.done(getAlarmCmdResponse)
.fail(function(jqxhr, textStatus, error) {
if (textstatus === "timeout") {
streamCmdQuery();
} else {
logAjaxFail(jqxhr, textStatus, error);
}
});
}
function getAlarmCmdResponse(respObj, respText) {
@ -533,11 +544,17 @@ function getAlarmCmdResponse(respObj, respText) {
}
function cmdDisableAlarms() {
alarmCmdReq.send(alarmCmdParms+"&command=disableAlarms");
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = 'disableAlarms';
alarmCmdReq(data);
}
function cmdEnableAlarms() {
alarmCmdReq.send(alarmCmdParms+"&command=enableAlarms");
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = 'enableAlarms';
alarmCmdReq(data);
}
function cmdAlarm() {
@ -549,17 +566,19 @@ function cmdAlarm() {
}
function cmdForceAlarm() {
alarmCmdReq.send(alarmCmdParms+"&command=forceAlarm");
if ( window.event ) {
window.event.preventDefault();
}
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = 'forceAlarm';
alarmCmdReq(data);
if ( window.event ) window.event.preventDefault();
}
function cmdCancelForcedAlarm() {
alarmCmdReq.send(alarmCmdParms+"&command=cancelForcedAlarm");
if ( window.event ) {
window.event.preventDefault();
}
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = 'cancelForcedAlarm';
alarmCmdReq(data);
if ( window.event ) window.event.preventDefault();
return false;
}
@ -571,18 +590,10 @@ function cmdForce() {
}
}
if ( monitorType != 'WebSite' ) {
var controlParms = 'view=request&request=control&id='+monitorId;
if ( auth_hash ) {
controlParms += '&auth='+auth_hash;
}
var controlReq = new Request.JSON( {
url: monitorUrl,
method: 'post',
timeout: AJAX_TIMEOUT,
link: 'cancel',
onSuccess: getControlResponse
} );
function controlReq(data) {
$j.getJSON(thisUrl + '?view=request&request=control&id='+monitorId, data)
.done(getControlResponse)
.fail(logAjaxFail);
}
function getControlResponse(respObj, respText) {
@ -601,7 +612,8 @@ function controlCmd(event) {
xtell = button.getAttribute('data-xtell');
ytell = button.getAttribute('data-ytell');
var locParms = '';
var data = {};
if ( event && (xtell || ytell) ) {
var target = event.target;
var offset = $j(target).offset();
@ -618,7 +630,7 @@ function controlCmd(event) {
} else if ( xtell == 2 ) {
xge = 2*(50 - xge);
}
locParms += '&xge='+xge;
data.xge = xge;
}
if ( ytell ) {
var yge = parseInt((y*100)/height);
@ -627,28 +639,35 @@ function controlCmd(event) {
} else if ( ytell == 2 ) {
yge = 2*(50 - yge);
}
locParms += '&yge='+yge;
data.yge = yge;
}
}
controlReq.send(controlParms+"&control="+control+locParms);
if ( auth_hash ) data.auth = auth_hash;
data.control = control;
controlReq(data);
if ( streamMode == 'single' ) {
fetchImage.pass($('imageFeed').getElement('img')).delay(1000);
setTimeout(fetchImage, 1000, $j('#imageFeed img'));
}
}
function controlCmdImage( x, y ) {
var imageControlParms = controlParms;
imageControlParms += '&scale='+scale;
imageControlParms += '&control='+imageControlMode;
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.scale = scale;
data.control = imageControlMode;
data.x = x;
data.y = y;
controlReq(data);
controlReq.send( imageControlParms+"&x="+x+"&y="+y );
if ( streamMode == 'single' ) {
fetchImage.pass( $('imageFeed').getElement('img') ).delay( 1000 );
setTimeout(fetchImage, 1000, $j('#imageFeed img'));
}
}
function fetchImage( streamImage ) {
streamImage.src = streamImage.src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) ));
streamImage.attr('src', streamImage.attr('src').replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) )));
}
function handleClick( event ) {
@ -772,7 +791,7 @@ function processClicks(event, field, value, row, $element) {
// Manage the DELETE CONFIRMATION modal button
function manageDelConfirmModalBtns() {
document.getElementById("delConfirmBtn").addEventListener("click", function onDelConfirmClick(evt) {
if ( ! canEditEvents ) {
if ( ! canEdit.Events ) {
enoperm();
return;
}
@ -795,7 +814,7 @@ function manageDelConfirmModalBtns() {
}
function initPage() {
if ( canViewControl ) {
if ( canView.Control ) {
// Load the PTZ Preset modal into the DOM
if ( monitorControllable ) getCtrlPresetModal();
// Load the settings modal into the DOM
@ -805,25 +824,25 @@ function initPage() {
if ( monitorType != 'WebSite' ) {
if ( streamMode == 'single' ) {
statusCmdTimer = statusCmdQuery.delay( (Math.random()+0.1)*statusRefreshTimeout );
watchdogCheck.pass('status').periodical(statusRefreshTimeout*2);
setInterval(watchdogCheck, statusRefreshTimeout*2, 'status');
} else {
streamCmdTimer = streamCmdQuery.delay( (Math.random()+0.1)*statusRefreshTimeout );
watchdogCheck.pass('stream').periodical(statusRefreshTimeout*2);
setInterval(watchdogCheck, statusRefreshTimeout*2, 'stream');
}
if ( canStreamNative || (streamMode == 'single') ) {
var streamImg = $('imageFeed').getElement('img');
var streamImg = $j('#imageFeed img');
if ( !streamImg ) {
streamImg = $('imageFeed').getElement('object');
streamImg = $j('#imageFeed object');
}
if ( !streamImg ) {
console.error('No streamImg found for imageFeed');
} else {
if ( streamMode == 'single' ) {
streamImg.addEvent('click', fetchImage.pass(streamImg));
fetchImage.pass(streamImg).periodical(imageRefreshTimeout);
streamImg.click(streamImg, fetchImage);
setInterval(fetchImage, imageRefreshTimeout, $j('#imageFeed img'));
} else {
streamImg.addEvent('click', function(event) {
streamImg.click(function(event) {
handleClick(event);
});
}
@ -866,7 +885,7 @@ function initPage() {
});
// Only enable the settings button for local cameras
settingsBtn.prop('disabled', !canViewControl);
settingsBtn.prop('disabled', !canView.Control);
if ( monitorType != 'Local' ) settingsBtn.hide();
// Init the bootstrap-table

View File

@ -73,9 +73,7 @@ var statusRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_STATUS ?>;
var eventsRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_EVENTS ?>;
var imageRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_IMAGE ?>;
var canEditMonitors = <?php echo canEdit( 'Monitors' )?'true':'false' ?>;
var canStreamNative = <?php echo canStreamNative()?'true':'false' ?>;
var canViewControl = <?php echo canView( 'Control' )?'true':'false' ?>;
var canPlayPauseAudio = Browser.ie;

View File

@ -128,7 +128,6 @@ var streamSrc = "<?php echo preg_replace( '/&amp;/', '&', $streamSrc ) ?>";
var statusRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_STATUS ?>;
var imageRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_IMAGE ?>;
var canEditMonitors = <?php echo canEdit( 'Monitors' )?'true':'false' ?>;
var canStreamNative = <?php echo canStreamNative()?'true':'false' ?>;
var canPlayPauseAudio = Browser.ie;

View File

@ -470,7 +470,7 @@ if ( canEdit('Monitors') ) {
<div class="tab-content" id="pills-tabContent">
<?php
foreach ( $tabs as $name=>$value ) {
echo '<div id="pills-'.$name.'" class="tab-pane fade'.($name==$tab ? ' show active' : '').'" role="tabpanel" area-labelledby="'.$name.'-tab">';
echo '<div id="pills-'.$name.'" class="tab-pane fade'.($name==$tab ? ' show active' : '').'" role="tabpanel" aria-labelledby="'.$name.'-tab">';
?>
<table class="major">
<tbody>
@ -502,6 +502,9 @@ switch ( $name ) {
<td class="text-right pr-3"><?php echo translate('SourceType') ?></td>
<td><?php echo htmlSelect('newMonitor[Type]', $sourceTypes, $monitor->Type()); ?></td>
</tr>
<?php
if ( $monitor->Type() != 'WebSite' ) {
?>
<tr>
<td class="text-right pr-3"><?php echo translate('Function') ?></td>
<td>
@ -512,15 +515,37 @@ switch ( $name ) {
}
echo htmlSelect('newMonitor[Function]', $function_options, $monitor->Function());
?>
<div id="function_help">
<?php
foreach ( ZM\getMonitorFunctionTypes() as $fn => $translated ) {
if ( isset($OLANG['FUNCTION_'.strtoupper($fn)]) ) {
echo '<div class="form-text" id="'.$fn.'Help">'.$OLANG['FUNCTION_'.strtoupper($fn)]['Help'].'</div>';
}
}
?>
</div>
</td>
</tr>
<tr>
<td class="text-right pr-3"><?php echo translate('Enabled') ?></td>
<td><input type="checkbox" name="newMonitor[Enabled]" value="1"<?php echo $monitor->Enabled() ? ' checked="checked"' : '' ?>/></td>
</tr>
<tr id="FunctionEnabled">
<td class="text-right pr-3"><?php echo translate('Analysis Enabled') ?></td>
<td><input type="checkbox" name="newMonitor[Enabled]" value="1"<?php echo $monitor->Enabled() ? ' checked="checked"' : '' ?>/>
<?php
if ( $monitor->Type() != 'WebSite' ) {
if ( isset($OLANG['FUNCTION_ANALYSIS_ENABLED']) ) {
echo '<div class="form-text">'.$OLANG['FUNCTION_ANALYSIS_ENABLED']['Help'].'</div>';
}
?>
</td>
</tr>
<tr id="FunctionDecodingEnabled">
<td class="text-right pr-3"><?php echo translate('Decoding Enabled') ?></td>
<td><input type="checkbox" name="newMonitor[DecodingEnabled]" value="1"<?php echo $monitor->DecodingEnabled() ? ' checked="checked"' : '' ?>/>
<?php
if ( isset($OLANG['FUNCTION_DECODING_ENABLED']) ) {
echo '<div class="form-text">'.$OLANG['FUNCTION_DECODING_ENABLED']['Help'].'</div>';
}
?>
</td>
</tr>
<tr>
<td class="text-right pr-3"><?php echo translate('LinkedMonitors'); echo makeHelpLink('OPTIONS_LINKED_MONITORS') ?></td>
<td>
@ -825,11 +850,14 @@ include('_monitor_source_nvsocket.php');
'320x240'=>'320x240',
'320x200'=>'320x200',
'352x240'=>'352x240 CIF',
'352x480'=>'352x480',
'640x480'=>'640x480',
'640x400'=>'640x400',
'704x240'=>'704x240 2CIF',
'704x480'=>'704x480 4CIF',
'720x480'=>'720x480 D1',
'704x576'=>'704x576 D1 PAL',
'720x480'=>'720x480 Full D1 NTSC',
'720x576'=>'720x576 Full D1 PAL',
'1280x720'=>'1280x720 720p',
'1280x800'=>'1280x800',
'1280x960'=>'1280x960 960p',