Merge branch 'master' into rtsp_server

This commit is contained in:
Isaac Connor 2020-12-15 10:14:19 -05:00
commit 99e7ae3505
45 changed files with 502 additions and 255 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

@ -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`, "
@ -272,6 +272,7 @@ Monitor::Monitor()
type(LOCAL),
function(NONE),
enabled(0),
decoding_enabled(0),
//protocol
//method
//options
@ -362,7 +363,7 @@ Monitor::Monitor()
/*
std::string load_monitor_sql =
"SELECT Id, Name, ServerId, StorageId, Type, Function+0, Enabled, LinkedMonitors, "
"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, RTSPDescribe, "
@ -409,6 +410,7 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
col++;
function = (Function)atoi(dbrow[col]); col++;
enabled = dbrow[col] ? atoi(dbrow[col]) : 0; col++;
decoding_enabled = dbrow[col] ? atoi(dbrow[col]) : 0; col++;
ReloadLinkedMonitors(dbrow[col]); col++;
@ -596,6 +598,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));
}
// Do this here to save a few cycles with all the comparisons
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 ) {
// FIXME Now that zma is a thread, this might not get called. Unless maybe we are redoing motion detection in a separate program.
while (
@ -2160,7 +2174,7 @@ void Monitor::ReloadZones() {
void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) {
Debug(1, "Reloading linked monitors for monitor %s, '%s'", name, p_linked_monitors);
if ( n_linked_monitors ) {
for( int i=0; i < n_linked_monitors; i++ ) {
for ( int i=0; i < n_linked_monitors; i++ ) {
delete linked_monitors[i];
}
delete[] linked_monitors;
@ -2489,7 +2503,6 @@ int Monitor::Capture() {
}
shared_data->signal = signal_check_points ? CheckSignal(capture_image) : true;
Debug(1, "signal check points? %d", signal_check_points);
shared_data->last_write_index = index;
shared_data->last_write_time = image_buffer[index].timestamp->tv_sec;
image_count++;
@ -2826,9 +2839,12 @@ int Monitor::PrimeCapture() {
video_stream_id = camera->get_VideoStreamId();
audio_stream_id = camera->get_AudioStreamId();
packetqueue = new zm_packetqueue(image_buffer_count, video_stream_id, audio_stream_id);
Debug(2, "Video stream id is %d, audio is %d, minimum_packets to keep in buffer %d",
video_stream_id, audio_stream_id, pre_event_buffer_count);
} else {
Debug(2, "Not Video stream id is %d, audio is %d, minimum_packets to keep in buffer %d",
video_stream_id, audio_stream_id, pre_event_buffer_count);
}
Debug(2, "Video stream id is %d, audio is %d, minimum_packets to keep in buffer %d",
video_stream_id, audio_stream_id, pre_event_buffer_count);
return ret;
}
@ -2839,6 +2855,7 @@ int Monitor::Close() {
delete packetqueue;
packetqueue = nullptr;
}
Debug(1, "Closing camera");
return camera->Close();
};
Monitor::Orientation Monitor::getOrientation() const { return orientation; }

View File

@ -245,6 +245,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

@ -48,14 +48,21 @@ zm_packetqueue::~zm_packetqueue() {
}
while ( !pktQueue.empty() ) {
Debug(1, "Fronting packet %d", pktQueue.empty());
ZMPacket * packet = pktQueue.front();
Debug(1, "poppng packet %d", packet->image_index);
pktQueue.pop_front();
delete packet;
if ( packet->image_index == -1 ) {
Debug(1, "Deletng packet");
delete packet;
}
}
delete[] packet_counts;
Debug(1, "Done in destrcutor");
packet_counts = nullptr;
mutex.unlock();
condition.notify_all();
}
/* Enqueues the given packet. Will maintain the analysis_it pointer and image packet counts.
@ -351,10 +358,13 @@ ZMPacket *zm_packetqueue::get_analysis_packet() {
Debug(1, "Locking in get_analysis_packet");
std::unique_lock<std::mutex> lck(mutex);
while ( ((! pktQueue.size()) || ( analysis_it == pktQueue.end() )) && !zm_terminate ) {
while ( ((! pktQueue.size()) or ( analysis_it == pktQueue.end() )) and !zm_terminate and !deleting ) {
Debug(2, "waiting. Queue size %d analysis_it == end? %d", pktQueue.size(), ( analysis_it == pktQueue.end() ) );
condition.wait(lck);
}
if ( deleting ) {
return nullptr;
}
//Debug(2, "Distance from head: (%d)", std::distance( pktQueue.begin(), analysis_it ) );
//Debug(2, "Distance from end: (%d)", std::distance( analysis_it, pktQueue.end() ) );
@ -364,6 +374,7 @@ ZMPacket *zm_packetqueue::get_analysis_packet() {
Debug(2,"waiting. Queue size %d analysis_it == end? %d", pktQueue.size(), ( analysis_it == pktQueue.end() ) );
condition.wait(lck);
if ( deleting ) {
Debug(1, "deleting");
// packetqueue is being deleted, do not assume we have a lock on the packet
return nullptr;
}

View File

@ -553,8 +553,8 @@ int ParseEncoderParameters(
}
valueoffset = line.find('=');
if ( valueoffset == std::string::npos || valueoffset+1 >= line.length() || valueoffset == 0 ) {
Warning("Failed parsing encoder parameters line %d: Invalid pair", lineno);
if ( valueoffset == std::string::npos || (valueoffset+1 >= line.length()) || (valueoffset == 0) ) {
Warning("Failed parsing encoder parameters line %d %s: Invalid pair", lineno, line.c_str());
continue;
}

View File

@ -319,14 +319,10 @@ int main(int argc, char *argv[]) {
if ( monitors[i]->PreCapture() < 0 ) {
Error("Failed to pre-capture monitor %d %d (%d/%d)",
monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors);
monitors[i]->Close();
result = -1;
break;
}
if ( monitors[i]->Capture() < 0 ) {
Error("Failed to capture image from monitor %d %s (%d/%d)",
monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors);
monitors[i]->Close();
Error("Failed to capture image from monitor %d %s (%d/%d)",
monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors);
result = -1;
@ -335,7 +331,6 @@ int main(int argc, char *argv[]) {
if ( monitors[i]->PostCapture() < 0 ) {
Error("Failed to post-capture monitor %d %s (%d/%d)",
monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors);
monitors[i]->Close();
result = -1;
break;
}
@ -383,6 +378,9 @@ int main(int argc, char *argv[]) {
zm_reload = false;
} // end if zm_reload
} // end while ! zm_terminate and connected
for ( int i = 0; i < n_monitors; i++ ) {
monitors[i]->Close();
}
delete [] alarm_capture_delays;
delete [] capture_delays;
@ -398,6 +396,7 @@ int main(int argc, char *argv[]) {
}
} // end foreach monitor
delete [] analysis_threads;
} // end while ! zm_terminate outer connection loop
Debug(1,"Updating Monitor status");

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

@ -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

@ -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

@ -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']);

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...
@ -1045,7 +1045,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;
}
@ -1134,13 +1134,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 +1183,7 @@ function initPage() {
// Manage the UNARCHIVE button
document.getElementById("unarchiveBtn").addEventListener("click", function onUnarchiveClick(evt) {
if ( ! canEditEvents ) {
if ( ! canEdit.Events ) {
enoperm();
return;
}
@ -1198,7 +1198,7 @@ function initPage() {
// Manage the EDIT button
document.getElementById("editBtn").addEventListener("click", function onEditClick(evt) {
if ( ! canEditEvents ) {
if ( ! canEdit.Events ) {
enoperm();
return;
}
@ -1253,7 +1253,7 @@ function initPage() {
// Manage the DELETE button
document.getElementById("deleteBtn").addEventListener("click", function onDeleteClick(evt) {
if ( ! canEditEvents ) {
if ( ! canEdit.Events ) {
enoperm();
return;
}

View File

@ -63,7 +63,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

@ -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 ) {
@ -269,7 +249,7 @@ function getStreamCmdResponse(respObj, respText) {
setButtonState('zoomOutBtn', 'inactive');
}
if ( canEditMonitors ) {
if ( canEdit.Monitors ) {
if ( streamStatus.enabled ) {
enableAlmBtn.addClass('disabled');
enableAlmBtn.prop('title', disableAlarmsStr);
@ -287,7 +267,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;
@ -298,10 +278,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 {
@ -341,7 +318,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);
}
}
@ -366,10 +346,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');
@ -381,7 +372,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');
@ -398,7 +392,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);
}
}
@ -413,7 +410,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 ) {
@ -432,7 +432,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 ) {
@ -451,43 +454,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) {
@ -511,23 +522,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) {
@ -535,11 +546,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() {
@ -551,17 +568,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;
}
@ -573,18 +592,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) {
@ -603,7 +614,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();
@ -620,7 +632,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);
@ -629,28 +641,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 ) {
@ -774,7 +793,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;
}
@ -797,7 +816,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
@ -807,25 +826,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);
});
}
@ -868,7 +887,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>