Merge branch 'storageareas' of github.com:connortechnology/ZoneMinder into storageareas

This commit is contained in:
Isaac Connor 2018-04-30 15:11:32 -04:00
commit cae2c9e237
55 changed files with 854 additions and 540 deletions

View File

@ -63,7 +63,7 @@ DROP TABLE IF EXISTS `Controls`;
CREATE TABLE `Controls` (
`Id` int(10) unsigned NOT NULL auto_increment,
`Name` varchar(64) NOT NULL default '',
`Type` enum('Local','Remote','Ffmpeg','Libvlc','cURL') NOT NULL default 'Local',
`Type` enum('Local','Remote','Ffmpeg','Libvlc','cURL','WebSite') NOT NULL default 'Local',
`Protocol` varchar(64) default NULL,
`CanWake` tinyint(3) unsigned NOT NULL default '0',
`CanSleep` tinyint(3) unsigned NOT NULL default '0',
@ -892,7 +892,7 @@ INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('4 Wide', '{ "default":{
INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('5 Wide', '{ "default":{"float":"left", "width":"19%","left":"0px","right":"0px","top":"0px","bottom":"0px"} }' );
-- We generally don't alter triggers, we drop and re-create them, so let's keep them in a separate file that we can just source in update scripts.
source @ZM_PATH_DATA@/db/triggers.sql
source @PKGDATADIR@/db/triggers.sql
--
-- Apply the initial configuration
--

24
db/zm_update-1.31.43.sql Normal file
View File

@ -0,0 +1,24 @@
--
-- This updates a 1.31.42 database to 1.31.43
--
-- Add WebSite enum to Monitor.Type
-- Add Refresh column to Monitors table
--
ALTER TABLE `zm`.`Monitors`
CHANGE COLUMN `Type` `Type` ENUM('Local', 'Remote', 'File', 'Ffmpeg', 'Libvlc', 'cURL', 'WebSite') NOT NULL DEFAULT 'Local' ;
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Monitors'
AND table_schema = DATABASE()
AND column_name = 'Refresh'
) > 0,
"SELECT 'Column Refresh exists in Monitors'",
"ALTER TABLE Monitors ADD `Refresh` int(10) unsigned default NULL"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

View File

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

View File

@ -54,7 +54,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,liburi-encode-perl
,libwww-perl
,libdata-uuid-perl
,libnumber-bytes-human
,libnumber-bytes-human-perl
,libfile-slurp-perl
,mysql-client | virtual-mysql-client
,perl-modules

View File

@ -71,6 +71,7 @@ The 1.2 at the start is basically adding 20% on top of the calculation to accoun
The math breakdown for 4 cameras running at 1280x960 capture, 50 frame buffer, 24 bit color space:
::
1280*960 = 1,228,800 (bytes)
1,228,800 * (3 bytes for 24 bit) = 3,686,400 (bytes)
3,686,400 * 50 = 184,320,000 (bytes)

View File

@ -14,7 +14,7 @@ If you are using an older version of ZoneMinder, please follow the legacy steps
**Step 2:** Mount your dedicated drive, partition, or network share to the local filesystem in any folder of your choosing.
We recommend you use systemd to manage the mount points.
Instructions on how to accomplish this can be found `here <https://zoneminder.blogspot.com/p/blog-page.html>`_ and `here <https://wiki.zoneminder.com/Common_Issues_with_Zoneminder_Installation_on_Ubuntu#Use_Systemd_to_Mount_Internal_Drive_or_NAS>`_.
Instructions on how to accomplish this can be found `here <https://zoneminder.blogspot.com/p/blog-page.html>`__ and `here <https://wiki.zoneminder.com/Common_Issues_with_Zoneminder_Installation_on_Ubuntu#Use_Systemd_to_Mount_Internal_Drive_or_NAS>`__.
Note that bind mounting ZoneMinder's images folder is optional. Newer version of ZoneMinder write very little, if anything, to the images folder.
Verify the dedicated drive, partition, or network share is successfully mounted before proceeding to the next step.

View File

@ -45,7 +45,7 @@ The following notes are based on real problems which have occurred by those who
How to Install ZoneMinder
-------------------------
ZoneMinder releases are now being hosted at RPM Fusion. New users should navigate the `RPM Fusion site <https://rpmfusion.org>`_ then follow the instructions to enable that repo. RHEL/CentOS users must also navaigate to the `EPEL Site <https://fedoraproject.org/wiki/EPEL>`_ and enable that repo as well. Once enabled, install ZoneMinder from the commandline:
ZoneMinder releases are now being hosted at RPM Fusion. New users should navigate the `RPM Fusion site <https://rpmfusion.org>`__ then follow the instructions to enable that repo. RHEL/CentOS users must also navaigate to the `EPEL Site <https://fedoraproject.org/wiki/EPEL>`_ and enable that repo as well. Once enabled, install ZoneMinder from the commandline:
::
@ -65,9 +65,9 @@ The feedback we get from those who use these development packages is extremely h
How to Change from Zmrepo to RPM Fusion
---------------------------------------
As mentioned above, the place to get the latest ZoneMinder release is now `RPM Fusion <https://rpmfusion.org>`_. If you are currently using ZoneMinder release packages from Zmrepo, then the following steps will change you over to RPM Fusion:
As mentioned above, the place to get the latest ZoneMinder release is now `RPM Fusion <https://rpmfusion.org>`__. If you are currently using ZoneMinder release packages from Zmrepo, then the following steps will change you over to RPM Fusion:
- Navigate to the `RPM Fusion site <https://rpmfusion.org>`_ and enable RPM Fusion on your system
- Navigate to the `RPM Fusion site <https://rpmfusion.org>`__ and enable RPM Fusion on your system
- Now issue the following from the command line:
::
@ -123,7 +123,7 @@ Your build environment is now set up.
Build from SRPM
***************
To continue, you need a ZoneMinder SRPM. If you wish to rebuild a ZoneMinder release, then browse the `RPM Fusion site <https://rpmfusion.org/>`_. If instead you wish to rebuild the latest source rpm from our master branch then browse the `Zmrepo site <http://zmrepo.zoneminder.com/>`_.
To continue, you need a ZoneMinder SRPM. If you wish to rebuild a ZoneMinder release, then browse the `RPM Fusion site <https://rpmfusion.org/>`__. If instead you wish to rebuild the latest source rpm from our master branch then browse the `Zmrepo site <http://zmrepo.zoneminder.com/>`_.
For this example, I'll use one of the source rpms from zmrepo:

View File

@ -147,6 +147,23 @@ Keep aspect ratio
Orientation
As per local devices.
WebSite
^^^^^^^
This Source Type allows one to configure an arbitrary website as a non-reocrdable, fully interactive, monitor in ZoneMinder. Note that sites with self-signed certificates will not display until the end user first manually navigates to the site and accpets the unsigned certificate. Also note that some sites will set an X-Frame option in the header, which discourages their site from being displayed within a frame. ZoneMinder will detect this condition and present a warning in the log. When this occurs, the end user can choose to install a browser plugin or extension to workaround this issue.
Website URL
Enter the full http or https url to the desired website.
Width (pixels)
Chose a desired width in pixels that gives an acceptable appearance. This may take some expirimentation.
Height (pixels)
Chose a desired height in pixels that gives an acceptable appearance. This may take some expirimentation.
Web Site Refresh
If the website in question has static content, optionally enter a time period in seconds for ZoneMinder to refresh the content.
Timestamp Tab
-------------

View File

@ -2946,6 +2946,23 @@ our @options = (
type => $types{boolean},
category => 'web',
},
{
name => 'ZM_WEB_XFRAME_WARN',
default => 'yes',
description => 'Warn when website X-Frame-Options is set to sameorigin',
help => q`
When creating a Web Site monitor, if the target web site has
X-Frame-Options set to sameorigin in the header, the site will
not display in ZoneMinder. This is a design feature in most modern
browsers. When this condiction has occured, ZoneMinder will write a
warning to the log file. To get around this, one can install a browser
plugin or extension to ignore X-Frame headers, and then the page will
display properly. Once the plugin or extenstion has ben installed,
the end user may choose to turn this warning off.
`,
type => $types{boolean},
category => 'web',
},
{
name => 'ZM_WEB_H_REFRESH_MAIN',
default => '60',

View File

@ -77,39 +77,39 @@ sub zmDbConnect {
}
my $options = shift;
if ( ( ! defined( $dbh ) ) or ! $dbh->ping() ) {
if ( ( !defined($dbh) ) or ! $dbh->ping() ) {
my ( $host, $portOrSocket ) = ( $ZoneMinder::Config::Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ );
my $socket;
if ( defined($portOrSocket) ) {
if ( $portOrSocket =~ /^\// ) {
$socket = ";mysql_socket=".$portOrSocket;
$socket = ';mysql_socket='.$portOrSocket;
} else {
$socket = ";host=".$host.";port=".$portOrSocket;
$socket = ';host='.$host.';port='.$portOrSocket;
}
} else {
$socket = ";host=".$Config{ZM_DB_HOST};
$socket = ';host='.$Config{ZM_DB_HOST};
}
my $sslOptions = "";
my $sslOptions = '';
if ( $Config{ZM_DB_SSL_CA_CERT} ) {
$sslOptions = ';'.join(';',
"mysql_ssl=1",
"mysql_ssl_ca_file=".$Config{ZM_DB_SSL_CA_CERT},
"mysql_ssl_client_key=".$Config{ZM_DB_SSL_CLIENT_KEY},
"mysql_ssl_client_cert=".$Config{ZM_DB_SSL_CLIENT_CERT}
'mysql_ssl=1',
'mysql_ssl_ca_file='.$Config{ZM_DB_SSL_CA_CERT},
'mysql_ssl_client_key='.$Config{ZM_DB_SSL_CLIENT_KEY},
'mysql_ssl_client_cert='.$Config{ZM_DB_SSL_CLIENT_CERT}
);
}
$dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
$dbh = DBI->connect( 'DBI:mysql:database='.$Config{ZM_DB_NAME}
.$socket . $sslOptions . ($options?';'.join(';', map { $_.'='.$$options{$_} } keys %{$options} ) : '' )
, $Config{ZM_DB_USER}
, $Config{ZM_DB_PASS}
);
$dbh->trace( 0 );
$dbh->trace(0) if $dbh;
}
return( $dbh );
}
return $dbh;
} # end sub zmDbConnect
sub zmDbDisconnect {
if ( defined( $dbh ) ) {

View File

@ -529,6 +529,8 @@ sub MoveTo {
my ( $NewPath ) = ( $NewStorage->Path() =~ /^(.*)$/ ); # De-taint
if ( ! $$NewStorage{Id} ) {
return "New storage does not have an id. Moving will not happen.";
} elsif ( $$NewStorage{Id} == $$self{StorageId} ) {
return "Event is already located at " . $NewPath;
} elsif ( !$NewPath ) {
return "New path ($NewPath) is empty.";
} elsif ( ! -e $NewPath ) {

View File

@ -160,9 +160,12 @@ sub Sql {
if ( $term->{attr} =~ /^Monitor/ ) {
my ( $temp_attr_name ) = $term->{attr} =~ /^Monitor(.+)$/;
$self->{Sql} .= 'M.'.$temp_attr_name;
} elsif ( $term->{attr} =~ /^Server/ ) {
$self->{Sql} .= 'S.'.$term->{attr};
} elsif ( $term->{attr} eq 'ServerId' or $term->{attr} eq 'MonitorServerId' ) {
$self->{Sql} .= 'M.ServerId';
} elsif ( $term->{attr} eq 'StorageServerId' ) {
$self->{Sql} .= 'S.ServerId';
} elsif ( $term->{attr} eq 'FilterServerId' ) {
$self->{Sql} .= $Config{ZM_SERVER_ID};
# StartTime options
} elsif ( $term->{attr} eq 'DateTime' ) {
$self->{Sql} .= 'E.StartTime';
@ -208,7 +211,7 @@ sub Sql {
foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) {
if ( $term->{attr} =~ /^MonitorName/ ) {
$value = "'$temp_value'";
} elsif ( $term->{attr} eq 'ServerId' ) {
} elsif ( $term->{attr} =~ /ServerId/) {
Debug("ServerId, temp_value is ($temp_value) ($ZoneMinder::Config::Config{ZM_SERVER_ID})");
if ( $temp_value eq 'ZM_SERVER_ID' ) {
$value = "'$ZoneMinder::Config::Config{ZM_SERVER_ID}'";

View File

@ -158,6 +158,7 @@ sub new {
( $this->{fileName} = $0 ) =~ s|^.*/||;
$this->{logPath} = $Config{ZM_PATH_LOGS};
$this->{logFile} = $this->{logPath}.'/'.$this->{id}.'.log';
($this->{logFile}) = $this->{logFile} =~ /^([\w\.\/]+)$/;
$this->{trace} = 0;
@ -207,6 +208,7 @@ sub initialise( @ ) {
if ( my $logFile = $this->getTargettedEnv('LOG_FILE') ) {
$tempLogFile = $logFile;
}
($tempLogFile) = $tempLogFile =~ /^([\w\.\/]+)$/;
my $tempLevel = INFO;
my $tempTermLevel = $this->{termLevel};
@ -581,7 +583,11 @@ sub logPrint {
syslog($priorities{$level}, $code.' [%s]', $string);
}
print($LOGFILE $message) if $level <= $this->{fileLevel};
print(STDERR $message) if $level <= $this->{termLevel};
if ( $level <= $this->{databaseLevel} ) {
if ( ( $this->{dbh} and $this->{dbh}->ping() ) or ( $this->{dbh} = zmDbConnect() ) ) {
my $sql = 'INSERT INTO Logs ( TimeKey, Component, Pid, Level, Code, Message, File, Line ) VALUES ( ?, ?, ?, ?, ?, ?, ?, NULL )';
$this->{sth} = $this->{dbh}->prepare_cached($sql);
if ( !$this->{sth} ) {
@ -598,11 +604,13 @@ sub logPrint {
);
if ( !$res ) {
$this->{databaseLevel} = NOLOG;
Error("Can't execute log entry '$sql': ".$this->{sth}->errstr());
Error("Can't execute log entry '$sql': ".$this->{dbh}->errstr());
}
}
} else {
print(STDERR "Can't log to database: ");
}
} # end if doing db logging
print(STDERR $message) if $level <= $this->{termLevel};
} # end if level < effectivelevel
}

View File

@ -242,6 +242,7 @@ use constant KILL_DELAY => 60; # seconds to wait between sending TERM and sendin
our %cmd_hash;
our %pid_hash;
our %terminating_processes;
our $zm_terminate = 0;
sub run {
my $fd = 0;
@ -276,9 +277,9 @@ sub run {
listen(SERVER, SOMAXCONN) or Fatal("Can't listen: $!");
$SIG{CHLD} = \&reaper;
$SIG{INT} = \&shutdownAll;
$SIG{TERM} = \&shutdownAll;
$SIG{ABRT} = \&shutdownAll;
$SIG{INT} = \&shutdown_sig_handler;
$SIG{TERM} = \&shutdown_sig_handler;
$SIG{ABRT} = \&shutdown_sig_handler;
$SIG{HUP} = \&logrot;
my $rin = '';
@ -295,12 +296,17 @@ sub run {
dPrint(ZoneMinder::Logger::INFO, 'Loading Server record have ' . $$Server{Name});
}
while( 1 ) {
while( !$zm_terminate ) {
if ( $Config{ZM_SERVER_ID} ) {
if ( ! ( $secs_count % 60 ) ) {
$dbh = zmDbConnect() if ! $dbh->ping();
Debug("Connecting");
while ( (!$zm_terminate) and !($dbh and $dbh->ping()) ) {
Warning("Not connected to db ($dbh). Reconnecting");
$dbh = zmDbConnect();
}
my @cpuload = CpuLoad();
Debug("UPdating Server record @cpuload");
if ( ! defined $dbh->do(q{UPDATE Servers SET Status=?,CpuLoad=?,TotalMem=?,FreeMem=?,TotalSwap=?,FreeSwap=? WHERE Id=?}, undef,
'Running', $cpuload[0], &totalmem, &freemem, &totalswap, &freeswap, $Config{ZM_SERVER_ID} ) ) {
Error("Failed Updating status of Server record for Id=$Config{ZM_SERVER_ID}".$dbh->errstr());
@ -308,7 +314,9 @@ sub run {
}
$secs_count += 1;
}
Debug("Before select");
my $nfound = select(my $rout = $rin, undef, undef, $timeout);
Debug("Aftere select $nfound");
if ( $nfound > 0 ) {
if ( vec($rout, fileno(SERVER), 1) ) {
my $paddr = accept(CLIENT, SERVER);
@ -330,7 +338,8 @@ sub run {
# Do nothing, this is all we're here for
dPrint(ZoneMinder::Logger::WARNING, "Already running, ignoring command '$command'\n");
} elsif ( $command eq 'shutdown' ) {
shutdownAll();
# Breka out of while loop
last;
} elsif ( $command eq 'check' ) {
check($daemon, @args);
} elsif ( $command eq 'status' ) {
@ -346,14 +355,13 @@ sub run {
}
close(CLIENT);
} else {
Fatal('Bogus descriptor');
Error('Bogus descriptor');
}
} elsif ( $nfound < 0 ) {
if ( $! == EINTR ) {
# Dead child, will be reaped
#print( "Probable dead child\n" );
# See if it needs to start up again
restartPending();
} elsif ( $! == EPIPE ) {
Error("Can't select: $!");
} else {
@ -361,12 +369,15 @@ sub run {
}
} else {
#print( "Select timed out\n" );
restartPending();
}
Debug("restartPending");
restartPending();
Debug("check_for_processes_to_kill");
check_for_processes_to_kill();
} # end while
dPrint(ZoneMinder::Logger::INFO, 'Server exiting at '
.strftime( '%y/%m/%d %H:%M:%S', localtime() )
."\n"
@ -377,9 +388,7 @@ sub run {
Error("Failed Updating status of Server record for Id=$Config{ZM_SERVER_ID}".$dbh->errstr());
}
}
unlink(main::SOCK_FILE) or Error('Unable to unlink ' . main::SOCK_FILE .". Error message was: $!") if ( -e main::SOCK_FILE );
unlink(ZM_PID) or Error('Unable to unlink ' . ZM_PID .". Error message was: $!") if ( -e ZM_PID );
exit();
shutdownAll();
}
sub cPrint {
@ -426,10 +435,12 @@ sub start {
}
my $sigset = POSIX::SigSet->new;
my $blockset = POSIX::SigSet->new( SIGCHLD );
my $blockset = POSIX::SigSet->new(SIGCHLD);
Debug("Blocking SIGCHLD");
sigprocmask(SIG_BLOCK, $blockset, $sigset) or Fatal("Can't block SIGCHLD: $!");
Debug("forking");
if ( my $cpid = fork() ) {
logReinit();
#logReinit();
$process->{pid} = $cpid;
$process->{started} = time();
@ -442,6 +453,7 @@ sub start {
$cmd_hash{$process->{command}} = $pid_hash{$cpid} = $process;
sigprocmask(SIG_SETMASK, $sigset) or Fatal("Can't restore SIGCHLD: $!");
Debug("unblocko child");
} elsif ( defined($cpid) ) {
# Force reconnection to the db.
$dbh = zmDbConnect(1);
@ -527,16 +539,18 @@ sub check_for_processes_to_kill {
my $sigset = POSIX::SigSet->new;
my $blockset = POSIX::SigSet->new(SIGCHLD);
sigprocmask(SIG_BLOCK, $blockset, $sigset) or die "dying at block...\n";
foreach my $command ( %terminating_processes ) {
foreach my $command ( keys %terminating_processes ) {
my $process = $cmd_hash{$command};
Debug("Have process $command at pid $$process{pid} $$process{term_sent_at}");
if ( $$process{term_sent_at} - time > KILL_DELAY ) {
my $now = time;
Debug("Have process $command at pid $$process{pid} $$process{term_sent_at} - $now = " . ( $$process{term_sent_at} - $now ) );
if ( $$process{term_sent_at} and ( $$process{term_sent_at} - $now > KILL_DELAY ) ) {
dPrint(ZoneMinder::Logger::WARNING, "'$$process{command}' has not stopped at "
.strftime('%y/%m/%d %H:%M:%S', localtime())
.' after ' . KILL_DELAY . ' seconds.'
." Sending KILL to pid $$process{pid}\n"
);
kill('KILL', $$process{pid});
delete $terminating_processes{$command};
}
}
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
@ -595,8 +609,14 @@ sub logrot {
}
}
sub shutdown_sig_handler {
$zm_terminate = 1;
}
sub reaper {
my $saved_status = $!;
# Wait for a child to terminate
while ( (my $cpid = waitpid(-1, WNOHANG)) > 0 ) {
my $status = $?;
@ -620,8 +640,8 @@ sub reaper {
my $out_str = "'$process->{command}' ";
if ( $exit_signal ) {
# 15 == TERM, 14 == ALARM
if ( $exit_signal == 15 || $exit_signal == 14 ) {
# TERM or ALRM
$out_str .= 'exited';
} else {
$out_str .= 'crashed';
@ -659,22 +679,26 @@ sub reaper {
$process->{delay} = $Config{ZM_MAX_RESTART_DELAY};
}
}
Debug("Delay for $$process{command} is now $$process{delay}");
} else {
delete $cmd_hash{$$process{command}};
}
}
} # end while waitpid
$SIG{CHLD} = \&reaper;
$! = $saved_status;
Debug("Leaving reaper");
}
sub restartPending {
# Restart any pending processes
foreach my $process ( values( %cmd_hash ) ) {
# Restart any pending processes, we list them first because cmd_hash may change in foreach
my @processes = values %cmd_hash;
foreach my $process ( @processes ) {
if ( $process->{pending} && $process->{pending} <= time() ) {
dPrint(ZoneMinder::Logger::INFO, "Starting pending process, $process->{command}\n");
start($process->{daemon}, @{$process->{args}});
}
}
dPrint(ZoneMinder::Logger::INFO, "done restartPending");
}
sub shutdownAll {
@ -683,9 +707,14 @@ sub shutdownAll {
next if ! $pid_hash{$pid};
send_stop(1, $pid_hash{$pid});
}
while ( %terminating_processes ) {
my $count= KILL_DELAY;
while ( (keys %terminating_processes) and $count) {
check_for_processes_to_kill();
sleep(1) if %terminating_processes;
if ( %terminating_processes ) {
Debug("Still " . %terminating_processes . ' to die. count is: ' . $count . ' sleeping');
sleep(1);
$count --;
}
}
dPrint(ZoneMinder::Logger::INFO, "Server shutdown at "
.strftime('%y/%m/%d %H:%M:%S', localtime())

View File

@ -94,7 +94,7 @@ use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|)
: ($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS})
;
logInit();
logInit($filter_id?(id=>'zmfilter_'.$filter_id):());
sub HupHandler {
Info("Received HUP, reloading");
&ZoneMinder::Logger::logHupHandler();
@ -154,38 +154,38 @@ my $delay = $Config{ZM_FILTER_EXECUTE_INTERVAL};
my $event_id = 0;
if ( ! EVENT_PATH ) {
Error( "No event path defined. Config was $Config{ZM_DIR_EVENTS}\n" );
Error("No event path defined. Config was $Config{ZM_DIR_EVENTS}\n");
die;
}
# In future, should not be neccessary wrt StorageAreas
chdir( EVENT_PATH );
# SHould not be neccessary... but nice to get a local var. What if it fails?
# Should not be neccessary... but nice to get a local var. What if it fails?
my $dbh = zmDbConnect();
if ( $filter_name ) {
Info( "Scanning for events using filter '$filter_name'\n" );
Info("Scanning for events using filter '$filter_name'\n");
} elsif ( $filter_id ) {
Info( "Scanning for events using filter id '$filter_id'\n" );
Info("Scanning for events using filter id '$filter_id'\n");
} else {
Info( "Scanning for events using all filters\n" );
Info("Scanning for events using all filters\n");
}
if ( ! ( $filter_name or $filter_id ) ) {
Debug("Sleeping due to start delay: " . START_DELAY . ' seconds...' );
sleep( START_DELAY );
Debug("Sleeping due to start delay: " . START_DELAY . ' seconds...');
sleep(START_DELAY);
}
my @filters;
my $last_action = 0;
while( ! $zm_terminate ) {
while( !$zm_terminate ) {
my $now = time;
if ( ($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY} ) {
Debug( "Reloading filters\n" );
Debug("Reloading filters\n");
$last_action = $now;
@filters = getFilters( { Name=>$filter_name, Id=>$filter_id } );
@filters = getFilters({ Name=>$filter_name, Id=>$filter_id });
}
foreach my $filter ( @filters ) {
@ -195,16 +195,16 @@ while( ! $zm_terminate ) {
my ( $id ) = $$filter{Id} =~ /(\d+)/;
Debug("Running concurrent filter process $proc --filter_id $$filter{Id} => $id for $$filter{Name}");
system( qq`$proc --filter "$$filter{Name}" &` );
system(qq`$proc --filter "$$filter{Name}" &`);
} else {
checkFilter( $filter );
checkFilter($filter);
}
}
last if $filter_name or $filter_id or $zm_terminate;
Debug( "Sleeping for $delay seconds\n" );
sleep( $delay );
Debug("Sleeping for $delay seconds\n");
sleep($delay);
}
sub getFilters {
@ -232,18 +232,18 @@ sub getFilters {
or UpdateDiskSpace = 1
or AutoMove = 1
) ORDER BY Name';
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Unable to prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( @sql_values )
or Fatal( "Unable to execute '$sql': ".$sth->errstr() );
my $sth = $dbh->prepare_cached($sql)
or Fatal("Unable to prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute(@sql_values)
or Fatal("Unable to execute '$sql': ".$dbh->errstr());
FILTER: while( my $db_filter = $sth->fetchrow_hashref() ) {
my $filter = new ZoneMinder::Filter( $$db_filter{Id}, $db_filter );
Debug( "Found filter '$db_filter->{Name}'\n" );
my $filter = new ZoneMinder::Filter($$db_filter{Id}, $db_filter);
Debug("Found filter '$db_filter->{Name}'");
# The undef here is to make sure the Sql gets regenerated because the Filter object may be cached
my $filter_sql = $filter->Sql(undef);
if ( ! $filter_sql ) {
Error( "Error parsing Sql. skipping filter '$db_filter->{Name}'\n" );
Error("Error parsing Sql. skipping filter '$db_filter->{Name}'");
next FILTER;
}
push @filters, $filter;
@ -252,7 +252,7 @@ FILTER: while( my $db_filter = $sth->fetchrow_hashref() ) {
if ( ! @filters ) {
Warning("No filter found for $sql with values(@sql_values)");
} else {
Debug( "Got " . @filters . " filters" );
Debug("Got " . @filters . " filters");
}
return @filters;
@ -283,56 +283,56 @@ sub checkFilter {
foreach my $event ( @Events ) {
last if $zm_terminate;
my $Event = new ZoneMinder::Event( $$event{Id}, $event );
my $Event = new ZoneMinder::Event($$event{Id}, $event);
Debug( "Checking event $event->{Id}" );
Debug("Checking event $event->{Id}");
my $delete_ok = !undef;
$dbh->ping();
if ( $filter->{AutoArchive} ) {
Info( "Archiving event $event->{Id}" );
Info("Archiving event $event->{Id}");
# Do it individually to avoid locking up the table for new events
my $sql = 'UPDATE Events SET Archived = 1 WHERE Id = ?';
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() );
or Fatal("Unable to prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute( $event->{Id} )
or Error( "Unable toexecute '$sql': ".$sth->errstr() );
or Error("Unable to execute '$sql': ".$dbh->errstr());
}
if ( $Config{ZM_OPT_FFMPEG} && $filter->{AutoVideo} ) {
if ( !$event->{Videoed} ) {
$delete_ok = undef if ( !generateVideo( $filter, $event ) );
$delete_ok = undef if !generateVideo($filter, $event);
}
}
if ( $Config{ZM_OPT_EMAIL} && $filter->{AutoEmail} ) {
if ( !$event->{Emailed} ) {
$delete_ok = undef if ( !sendEmail( $filter, $Event ) );
$delete_ok = undef if !sendEmail($filter, $Event);
}
}
if ( $Config{ZM_OPT_MESSAGE} && $filter->{AutoMessage} ) {
if ( !$event->{Messaged} ) {
$delete_ok = undef if ( !sendMessage( $filter, $event ) );
$delete_ok = undef if !sendMessage($filter, $event);
}
}
if ( $Config{ZM_OPT_UPLOAD} && $filter->{AutoUpload} ) {
if ( !$event->{Uploaded} ) {
$delete_ok = undef if ( !uploadArchFile( $filter, $event ) );
$delete_ok = undef if !uploadArchFile($filter, $event);
}
}
if ( $filter->{AutoExecute} ) {
if ( !$event->{Executed} ) {
$delete_ok = undef if ( !executeCommand( $filter, $event ) );
$delete_ok = undef if !executeCommand($filter, $event);
}
}
if ( $filter->{AutoDelete} ) {
if ( $delete_ok ) {
$Event->delete();
} else {
Error( "Unable toto delete event $event->{Id} as previous operations failed\n" );
Error("Unable toto delete event $event->{Id} as previous operations failed");
}
} # end if AutoDelete
if ( $filter->{AutoMove} ) {
my $NewStorage = new ZoneMinder::Storage( $filter->{AutoMoveTo} );
$_ = $Event->MoveTo( $NewStorage );
my $NewStorage = new ZoneMinder::Storage($filter->{AutoMoveTo});
$_ = $Event->MoveTo($NewStorage);
Error($_) if $_;
}
@ -364,7 +364,7 @@ sub generateVideo {
my $scale = $event->{DefaultScale}/100;
my $format;
my @ffmpeg_formats = split( /\s+/, $Config{ZM_FFMPEG_FORMATS} );
my @ffmpeg_formats = split(/\s+/, $Config{ZM_FFMPEG_FORMATS});
my $default_video_format;
my $default_phone_format;
foreach my $ffmpeg_format( @ffmpeg_formats ) {
@ -408,10 +408,10 @@ sub generateVideo {
return 0;
} else {
my $sql = 'UPDATE Events SET Videoed = 1 WHERE Id = ?';
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Unable to prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute( $event->{Id} )
or Fatal("Unable toexecute '$sql': ".$sth->errstr());
my $sth = $dbh->prepare_cached($sql)
or Fatal("Unable to prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute($event->{Id})
or Fatal("Unable toexecute '$sql': ".$dbh->errstr());
if ( wantarray() ) {
return( $format, $output );
}
@ -463,12 +463,12 @@ sub uploadArchFile {
my $event = shift;
if ( ! $Config{ZM_UPLOAD_HOST} ) {
Error( 'Cannot upload archive as no upload host defined' );
Error('Cannot upload archive as no upload host defined');
return( 0 );
}
my $archFile = $event->{MonitorName}.'-'.$event->{Id};
my $archImagePath = getEventPath( $event )
my $archImagePath = getEventPath($event)
.'/'
.(
( $Config{ZM_UPLOAD_ARCH_ANALYSE} )
@ -485,14 +485,14 @@ sub uploadArchFile {
$archFile .= '.zip';
$archLocPath = $Config{ZM_UPLOAD_LOC_DIR}.'/'.$archFile;
my $zip = Archive::Zip->new();
Info( "Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n" );
Info("Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n");
my $status = &AZ_OK;
foreach my $imageFile ( @archImageFiles ) {
Debug( "Adding $imageFile\n" );
my $member = $zip->addFile( $imageFile );
Debug("Adding $imageFile\n");
my $member = $zip->addFile($imageFile);
if ( !$member ) {
Error( "Unable toto add image file $imageFile to zip archive $archLocPath" );
Error("Unable toto add image file $imageFile to zip archive $archLocPath");
$archError = 1;
last;
}
@ -505,10 +505,10 @@ sub uploadArchFile {
$status = $zip->writeToFileNamed( $archLocPath );
if ( $archError = ($status != &AZ_OK) ) {
Error( "Zip error: $status\n " );
Error("Zip error: $status");
}
} else {
Error( "Error adding images to zip archive $archLocPath, not writing" );
Error("Error adding images to zip archive $archLocPath, not writing");
}
} elsif ( $Config{ZM_UPLOAD_ARCH_FORMAT} eq 'tar' ) {
if ( $Config{ZM_UPLOAD_ARCH_COMPRESS} ) {
@ -517,7 +517,7 @@ sub uploadArchFile {
$archFile .= '.tar';
}
$archLocPath = $Config{ZM_UPLOAD_LOC_DIR}.'/'.$archFile;
Info( "Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n" );
Info("Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n");
if ( $archError = !Archive::Tar->create_archive(
$archLocPath,
@ -525,7 +525,7 @@ sub uploadArchFile {
@archImageFiles
)
) {
Error( 'Tar error: '.Archive::Tar->error()."\n " );
Error('Tar error: '.Archive::Tar->error());
}
}
@ -533,7 +533,7 @@ sub uploadArchFile {
return( 0 );
} else {
if ( $Config{ZM_UPLOAD_PROTOCOL} eq 'ftp' ) {
Info( 'Uploading to '.$Config{ZM_UPLOAD_HOST}." using FTP\n" );
Info('Uploading to '.$Config{ZM_UPLOAD_HOST}." using FTP");
my $ftp = Net::FTP->new(
$Config{ZM_UPLOAD_HOST},
Timeout=>$Config{ZM_UPLOAD_TIMEOUT},
@ -541,58 +541,56 @@ sub uploadArchFile {
Debug=>$Config{ZM_UPLOAD_DEBUG}
);
if ( !$ftp ) {
Error( "Unable tocreate FTP connection: $@" );
return( 0 );
Error("Unable tocreate FTP connection: $@");
return 0;
}
$ftp->login( $Config{ZM_UPLOAD_USER}, $Config{ZM_UPLOAD_PASS} )
or Error( "FTP - Unable tologin" );
$ftp->login($Config{ZM_UPLOAD_USER}, $Config{ZM_UPLOAD_PASS})
or Error("FTP - Unable tologin");
$ftp->binary()
or Error( "FTP - Unable togo binary" );
$ftp->cwd( $Config{ZM_UPLOAD_REM_DIR} )
or Error( "FTP - Unable tocwd" )
or Error("FTP - Unable togo binary");
$ftp->cwd($Config{ZM_UPLOAD_REM_DIR})
or Error("FTP - Unable tocwd")
if ( $Config{ZM_UPLOAD_REM_DIR} );
$ftp->put( $archLocPath )
or Error( "FTP - Unable toupload '$archLocPath'" );
or Error("FTP - Unable toupload '$archLocPath'");
$ftp->quit()
or Error( "FTP - Unable toquit" );
or Error("FTP - Unable toquit");
} else {
my $host = $Config{ZM_UPLOAD_HOST};
$host .= ':'.$Config{ZM_UPLOAD_PORT}
if $Config{ZM_UPLOAD_PORT};
Info( 'Uploading to '.$host." using SFTP\n" );
my %sftpOptions = ( host=>$Config{ZM_UPLOAD_HOST}, user=>$Config{ZM_UPLOAD_USER} );
$sftpOptions{password} = $Config{ZM_UPLOAD_PASS}
if $Config{ZM_UPLOAD_PASS};
$sftpOptions{port} = $Config{ZM_UPLOAD_PORT}
if $Config{ZM_UPLOAD_PORT};
$sftpOptions{timeout} = $Config{ZM_UPLOAD_TIMEOUT}
if $Config{ZM_UPLOAD_TIMEOUT};
$host .= ':'.$Config{ZM_UPLOAD_PORT} if $Config{ZM_UPLOAD_PORT};
Info('Uploading to '.$host." using SFTP\n");
my %sftpOptions = (
host=>$Config{ZM_UPLOAD_HOST}, user=>$Config{ZM_UPLOAD_USER}
($Config{ZM_UPLOAD_PASS} ? (password=>$Config{ZM_UPLOAD_PASS}) : ()),
($Config{ZM_UPLOAD_PORT} ? (port=>$Config{ZM_UPLOAD_PORT}) : ()),
($Config{ZM_UPLOAD_TIMEOUT} ? (timeout=>$Config{ZM_UPLOAD_TIMEOUT}) : ()),
);
my @more_ssh_args;
push @more_ssh_args, '-o'=>'StrictHostKeyChecking=no'
if ! $Config{ZM_UPLOAD_STRICT};
push @more_ssh_args, '-v'
if $Config{ZM_UPLOAD_DEBUG};
$sftpOptions{more} = [@more_ssh_args];
my $sftp = Net::SFTP::Foreign->new( $Config{ZM_UPLOAD_HOST}, %sftpOptions );
my $sftp = Net::SFTP::Foreign->new($Config{ZM_UPLOAD_HOST}, %sftpOptions);
if ( $sftp->error ) {
Error( "Unable tocreate SFTP connection: ".$sftp->error );
return( 0 );
Error("Unable tocreate SFTP connection: ".$sftp->error);
return 0;
}
$sftp->setcwd( $Config{ZM_UPLOAD_REM_DIR} )
or Error( "SFTP - Unable tosetcwd: ".$sftp->error )
$sftp->setcwd($Config{ZM_UPLOAD_REM_DIR})
or Error("SFTP - Unable to setcwd: ".$sftp->error)
if $Config{ZM_UPLOAD_REM_DIR};
$sftp->put( $archLocPath, $archFile )
or Error( "SFTP - Unable toupload '$archLocPath': ".$sftp->error );
$sftp->put($archLocPath, $archFile)
or Error("SFTP - Unable to upload '$archLocPath': ".$sftp->error);
}
unlink( $archLocPath );
unlink($archLocPath);
my $sql = 'UPDATE Events SET Uploaded = 1 WHERE Id = ?';
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} )
or Fatal( "Unable toexecute '$sql': ".$sth->errstr() );
my $sth = $dbh->prepare_cached($sql)
or Fatal("Unable toprepare '$sql': ".$dbh->errstr());
my $res = $sth->execute($event->{Id})
or Fatal("Unable toexecute '$sql': ".$dbh->errstr());
}
return( 1 );
}
return 1;
} # end sub uploadArchFile
sub substituteTags {
my $text = shift;
@ -613,14 +611,13 @@ sub substituteTags {
my $max_alarm_frame;
my $max_alarm_score = 0;
if ( $need_images ) {
my $sql = "SELECT * FROM Frames
my $sql = q`SELECT * FROM Frames
WHERE EventId = ? AND Type = 'Alarm'
ORDER BY FrameId"
;
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $Event->{Id} )
or Fatal( "Unable toexecute '$sql': ".$sth->errstr() );
ORDER BY FrameId`;
my $sth = $dbh->prepare_cached($sql)
or Fatal("Unable toprepare '$sql': ".$dbh->errstr());
my $res = $sth->execute($Event->{Id})
or Fatal( "Unable toexecute '$sql': ".$dbh->errstr());
my $rows = 0;
while( my $frame = $sth->fetchrow_hashref() ) {
if ( !$first_alarm_frame ) {
@ -646,8 +643,8 @@ sub substituteTags {
$text =~ s/%MEM%/$Monitor->{MonthEvents}/g;
$text =~ s/%MEA%/$Monitor->{ArchivedEvents}/g;
$text =~ s/%MP%/$url?view=watch&mid=$Event->{MonitorId}/g;
$text =~ s/%MPS%/$url?view=watchfeed&mid=$Event->{MonitorId}&mode=stream/g;
$text =~ s/%MPI%/$url?view=watchfeed&mid=$Event->{MonitorId}&mode=still/g;
$text =~ s/%MPS%/$url?view=watch&mid=$Event->{MonitorId}&mode=stream/g;
$text =~ s/%MPI%/$url?view=watch&mid=$Event->{MonitorId}&mode=still/g;
$text =~ s/%EP%/$url?view=event&mid=$Event->{MonitorId}&eid=$Event->{Id}/g;
$text =~ s/%EPS%/$url?view=event&mode=stream&mid=$Event->{MonitorId}&eid=$Event->{Id}/g;
$text =~ s/%EPI%/$url?view=event&mode=still&mid=$Event->{MonitorId}&eid=$Event->{Id}/g;
@ -697,13 +694,13 @@ sub substituteTags {
if ( $attachments_ref && $text =~ s/%EIMA%//g ) {
# Don't attach the same image twice
if ( !@$attachments_ref
|| ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} )
|| ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId})
) {
my $path = generateImage($Event, $max_alarm_frame, 'analyse');
if ( -e $path ) {
push @$attachments_ref, { type=>'image/jpeg', path=>$path };
} else {
Warning("No image for EIMA");
Warning('No image for EIMA');
}
}
}
@ -737,7 +734,7 @@ sub sendEmail {
my $Event = shift;
if ( ! $Config{ZM_FROM_EMAIL} ) {
Error("No 'from' email address defined, not sending email");
Error('No from email address defined, not sending email');
return 0;
}
if ( ! $Config{ZM_EMAIL_ADDRESS} ) {
@ -745,7 +742,7 @@ sub sendEmail {
return 0;
}
Info("Creating notification email\n");
Info('Creating notification email');
my $subject = substituteTags($Config{ZM_EMAIL_SUBJECT}, $filter, $Event);
return 0 if !$subject;
@ -753,7 +750,7 @@ sub sendEmail {
my $body = substituteTags($Config{ZM_EMAIL_BODY}, $filter, $Event, \@attachments);
return 0 if !$body;
Info("Sending notification email '$subject'\n");
Info("Sending notification email '$subject'");
eval {
if ( $Config{ZM_NEW_MAIL_MODULES} ) {
@ -771,7 +768,7 @@ sub sendEmail {
);
### Add the attachments
foreach my $attachment ( @attachments ) {
Info( "Attaching '$attachment->{path}\n" );
Info( "Attaching '$attachment->{path}'" );
$mail->attach(
Path => $attachment->{path},
Type => $attachment->{type},
@ -783,20 +780,20 @@ sub sendEmail {
my $ssmtp_location = $Config{ZM_SSMTP_PATH};
if ( !$ssmtp_location ) {
if ( logDebugging() ) {
Debug( "which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message\n" );
Debug("which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message");
}
$ssmtp_location = qx('which ssmtp');
}
if ( !$ssmtp_location ) {
Debug( "Unable tofind ssmtp, trying MIME::Lite->send" );
MIME::Lite->send( 'smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60 );
Debug('Unable tofind ssmtp, trying MIME::Lite->send');
MIME::Lite->send('smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60);
$mail->send();
} else {
### Send using SSMTP
$mail->send( 'sendmail', $ssmtp_location, $Config{ZM_EMAIL_ADDRESS} );
$mail->send('sendmail', $ssmtp_location, $Config{ZM_EMAIL_ADDRESS});
}
} else {
MIME::Lite->send( 'smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60 );
MIME::Lite->send('smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60);
$mail->send();
}
} else {
@ -809,29 +806,29 @@ sub sendEmail {
);
foreach my $attachment ( @attachments ) {
Info( "Attaching '$attachment->{path}\n" );
Info("Attaching '$attachment->{path}'");
$mail->attach(
Path => $attachment->{path},
Type => $attachment->{type},
Encoding => 'base64'
);
}
$mail->smtpsend( Host => $Config{ZM_EMAIL_HOST}, MailFrom => $Config{ZM_FROM_EMAIL} );
$mail->smtpsend(Host => $Config{ZM_EMAIL_HOST}, MailFrom => $Config{ZM_FROM_EMAIL});
}
};
if ( $@ ) {
Error( "Unable tosend email: $@" );
return( 0 );
Error("Unable tosend email: $@");
return 0;
} else {
Info( "Notification email sent\n" );
Info('Notification email sent');
}
my $sql = 'update Events set Emailed = 1 where Id = ?';
my $sql = 'UPDATE Events SET Emailed = 1 WHERE Id = ?';
my $sth = $dbh->prepare_cached($sql)
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() );
or Fatal("Unable toprepare '$sql': ".$dbh->errstr());
my $res = $sth->execute($Event->{Id})
or Fatal( "Unable toexecute '$sql': ".$sth->errstr() );
or Fatal("Unable toexecute '$sql': ".$dbh->errstr());
return( 1 );
return 1;
}
sub sendMessage {
@ -839,28 +836,28 @@ sub sendMessage {
my $event = shift;
if ( ! $Config{ZM_FROM_EMAIL} ) {
Error( "No 'from' email address defined, not sending message" );
return( 0 );
Error('No from email address defined, not sending message');
return 0;
}
if ( ! $Config{ZM_MESSAGE_ADDRESS} ) {
Error( 'No message address defined, not sending message' );
return( 0 );
Error('No message address defined, not sending message');
return 0;
}
Info( "Creating notification message\n" );
Info('Creating notification message');
my $subject = substituteTags( $Config{ZM_MESSAGE_SUBJECT}, $filter, $event );
return( 0 ) if ( !$subject );
my $subject = substituteTags($Config{ZM_MESSAGE_SUBJECT}, $filter, $event);
return 0 if !$subject;
my @attachments;
my $body = substituteTags( $Config{ZM_MESSAGE_BODY}, $filter, $event, \@attachments );
return( 0 ) if ( !$body );
my $body = substituteTags($Config{ZM_MESSAGE_BODY}, $filter, $event, \@attachments);
return 0 if !$body;
Info( "Sending notification message '$subject'\n" );
Info("Sending notification message '$subject'");
eval {
if ( $Config{ZM_NEW_MAIL_MODULES} ) {
### Create the multipart container
my $mail = MIME::Lite->new (
my $mail = MIME::Lite->new(
From => $Config{ZM_FROM_EMAIL},
To => $Config{ZM_MESSAGE_ADDRESS},
Subject => $subject,
@ -873,7 +870,7 @@ sub sendMessage {
);
### Add the attachments
foreach my $attachment ( @attachments ) {
Info( "Attaching '$attachment->{path}\n" );
Info("Attaching '$attachment->{path}");
$mail->attach(
Path => $attachment->{path},
Type => $attachment->{type},
@ -885,20 +882,20 @@ sub sendMessage {
my $ssmtp_location = $Config{ZM_SSMTP_PATH};
if ( !$ssmtp_location ) {
if ( logDebugging() ) {
Debug( "which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message\n" );
Debug("which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message");
}
$ssmtp_location = qx('which ssmtp');
}
if ( !$ssmtp_location ) {
Debug( 'Unable tofind ssmtp, trying MIME::Lite->send' );
MIME::Lite->send( 'smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60 );
Debug('Unable tofind ssmtp, trying MIME::Lite->send');
MIME::Lite->send(smtp=>$Config{ZM_EMAIL_HOST}, Timeout=>60);
$mail->send();
} else {
### Send using SSMTP
$mail->send( 'sendmail', $ssmtp_location, $Config{ZM_MESSAGE_ADDRESS} );
$mail->send('sendmail', $ssmtp_location, $Config{ZM_MESSAGE_ADDRESS});
}
} else {
MIME::Lite->send( 'smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60 );
MIME::Lite->send(smtp=>$Config{ZM_EMAIL_HOST}, Timeout=>60);
$mail->send();
}
} else {
@ -911,59 +908,60 @@ sub sendMessage {
);
foreach my $attachment ( @attachments ) {
Info( "Attaching '$attachment->{path}\n" );
Info("Attaching '$attachment->{path}'");
$mail->attach(
Path => $attachment->{path},
Type => $attachment->{type},
Encoding => 'base64'
);
}
$mail->smtpsend( Host => $Config{ZM_EMAIL_HOST},
$mail->smtpsend(
Host => $Config{ZM_EMAIL_HOST},
MailFrom => $Config{ZM_FROM_EMAIL}
);
}
};
if ( $@ ) {
Error( "Unable tosend email: $@" );
return( 0 );
Error("Unable tosend email: $@");
return 0;
} else {
Info( "Notification message sent\n" );
Info('Notification message sent');
}
my $sql = 'update Events set Messaged = 1 where Id = ?';
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} )
or Fatal( "Unable toexecute '$sql': ".$sth->errstr() );
my $sql = 'UPDATE Events SET Messaged = 1 WHERE Id = ?';
my $sth = $dbh->prepare_cached($sql)
or Fatal("Unable toprepare '$sql': ".$dbh->errstr());
my $res = $sth->execute($event->{Id})
or Fatal("Unable toexecute '$sql': ".$dbh->errstr());
return( 1 );
return 1;
}
sub executeCommand {
my $filter = shift;
my $event = shift;
my $event_path = getEventPath( $event );
my $event_path = getEventPath($event);
my $command = $filter->{AutoExecuteCmd};
$command .= " $event_path";
$command = substituteTags( $command, $filter, $event );
$command = substituteTags($command, $filter, $event);
Info( "Executing '$command'\n" );
Info("Executing '$command'");
my $output = qx($command);
my $status = $? >> 8;
if ( $status || logDebugging() ) {
chomp( $output );
Debug( "Output: $output\n" );
chomp($output);
Debug("Output: $output");
}
if ( $status ) {
Error( "Command '$command' exited with status: $status\n" );
return( 0 );
Error("Command '$command' exited with status: $status");
return 0;
} else {
my $sql = 'update Events set Executed = 1 where Id = ?';
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() );
my $sql = 'UPDATE Events SET Executed = 1 WHERE Id = ?';
my $sth = $dbh->prepare_cached($sql)
or Fatal("Unable to prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute( $event->{Id} )
or Fatal( "Unable toexecute '$sql': ".$sth->errstr() );
or Fatal("Unable to execute '$sql': ".$dbh->errstr());
}
return( 1 );
}

View File

@ -211,7 +211,7 @@ if ( $command =~ /^(?:start|restart)$/ ) {
my $res = $sth->execute( @values )
or Fatal( "Can't execute: ".$sth->errstr() );
while( my $monitor = $sth->fetchrow_hashref() ) {
if ( $monitor->{Function} ne 'None' ) {
if ( $monitor->{Function} ne 'None' && $monitor->{Type} ne 'WebSite' ) {
if ( $monitor->{Type} eq 'Local' ) {
runCommand( "zmdc.pl start zmc -d $monitor->{Device}" );
} else {

View File

@ -84,6 +84,7 @@ while( 1 ) {
while( my $monitor = $sth->fetchrow_hashref() ) {
my $now = time();
next if $monitor->{Function} eq 'None';
next if $monitor->{Type} eq 'WebSite';
my $restart = 0;
if ( zmMemVerify( $monitor ) ) {
# Check we have got an image recently

View File

@ -69,7 +69,9 @@ Event::Event(
untimedEvent = true;
start_time = now;
} else if ( start_time.tv_sec > now.tv_sec ) {
Error("StartTime in the future");
Error("StartTime in the future %d.%d > $d.%d",
start_time.tv_sec, start_time.tv_usec, now.tv_sec, now.tv_usec
);
start_time = now;
}
@ -257,7 +259,7 @@ Event::~Event() {
"UPDATE Events SET Name='%s %" PRIu64 "', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' WHERE Id = %" PRIu64,
monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, video_name, id );
db_mutex.lock();
while ( mysql_query(&dbconn, sql) ) {
while ( mysql_query(&dbconn, sql) && !zm_terminate ) {
Error("Can't update event: %s reason: %s", sql, mysql_error(&dbconn));
db_mutex.unlock();
sleep(1);
@ -422,16 +424,16 @@ void Event::updateNotes( const StringSetMap &newNoteSetMap ) {
#else
static char escapedNotes[ZM_SQL_MED_BUFSIZ];
mysql_real_escape_string( &dbconn, escapedNotes, notes.c_str(), notes.length() );
mysql_real_escape_string(&dbconn, escapedNotes, notes.c_str(), notes.length());
snprintf( sql, sizeof(sql), "UPDATE Events SET Notes = '%s' WHERE Id = %" PRIu64, escapedNotes, id );
snprintf(sql, sizeof(sql), "UPDATE Events SET Notes = '%s' WHERE Id = %" PRIu64, escapedNotes, id);
db_mutex.lock();
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't insert event: %s", mysql_error( &dbconn ) );
if ( mysql_query(&dbconn, sql) ) {
Error("Can't insert event: %s", mysql_error(&dbconn));
}
db_mutex.unlock();
#endif
}
} // end if update
}
void Event::AddFrames( int n_frames, Image **images, struct timeval **timestamps ) {
@ -485,7 +487,7 @@ void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, st
snprintf( sql+sql_len, sizeof(sql)-sql_len, "( %" PRIu64 ", %d, from_unixtime(%ld), %s%ld.%02ld ), ", id, frames, timestamps[i]->tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec );
frameCount++;
}
} // end foreach frame
if ( frameCount ) {
Debug( 1, "Adding %d/%d frames to DB", frameCount, n_frames );
@ -543,7 +545,10 @@ Debug(3, "Writing video");
Debug( 1, "Adding frame %d of type \"%s\" to DB", frames, Event::frame_type_names[frame_type] );
static char sql[ZM_SQL_MED_BUFSIZ];
snprintf(sql, sizeof(sql), "INSERT INTO Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score ) values ( %" PRIu64 ", %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d )", id, frames, frame_type_names[frame_type], timestamp.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, score);
snprintf(sql, sizeof(sql),
"INSERT INTO Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score )"
" VALUES ( %" PRIu64 ", %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d )",
id, frames, frame_type_names[frame_type], timestamp.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, score);
db_mutex.lock();
if ( mysql_query(&dbconn, sql) ) {
Error("Can't insert frame: %s", mysql_error(&dbconn));
@ -556,7 +561,7 @@ Debug(3, "Writing video");
// We are writing a Bulk frame
if ( frame_type == BULK ) {
snprintf( sql, sizeof(sql),
snprintf(sql, sizeof(sql),
"UPDATE Events SET Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %" PRIu64,
( delta_time.positive?"":"-" ),
delta_time.sec, delta_time.fsec,
@ -568,7 +573,7 @@ Debug(3, "Writing video");
id
);
db_mutex.lock();
while ( mysql_query(&dbconn, sql) ) {
while ( mysql_query(&dbconn, sql) && !zm_terminate ) {
Error("Can't update event: %s", mysql_error(&dbconn));
db_mutex.unlock();
sleep(1);

View File

@ -27,6 +27,7 @@
#include <getopt.h>
#include <arpa/inet.h>
#include <glob.h>
#include <cinttypes>
#include "zm.h"
#include "zm_db.h"

View File

@ -16,6 +16,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <cinttypes>
#include "zm_ffmpeg.h"
#include "zm_image.h"

View File

@ -18,6 +18,7 @@
//
#include "zm.h"
#include "zm_signal.h"
#if HAVE_LIBAVFORMAT
@ -180,7 +181,7 @@ int FfmpegCamera::Capture( Image &image ) {
// If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread.
int frameComplete = false;
while ( !frameComplete ) {
while ( !frameComplete && !zm_terminate) {
int avResult = av_read_frame(mFormatContext, &packet);
char errbuf[AV_ERROR_MAX_STRING_SIZE];
if ( avResult < 0 ) {
@ -295,12 +296,12 @@ int FfmpegCamera::Capture( Image &image ) {
bytes += packet.size;
zm_av_packet_unref( &packet );
} // end while ! frameComplete
return 1;
return frameComplete ? 1 : 0;
} // FfmpegCamera::Capture
int FfmpegCamera::PostCapture() {
// Nothing to do here
return( 0 );
return 0;
}
int FfmpegCamera::OpenFfmpeg() {

View File

@ -543,7 +543,7 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
mysql_real_escape_string( &dbconn, escapedString, syslogStart, strlen(syslogStart) );
snprintf( sql, sizeof(sql), "insert into Logs ( TimeKey, Component, ServerId, Pid, Level, Code, Message, File, Line ) values ( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )", timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString, file, line );
if (mysql_query(&dbconn, sql)) {
if ( mysql_query(&dbconn, sql) ) {
Level tempDatabaseLevel = mDatabaseLevel;
databaseLevel(NOLOG);
Error("Can't insert log entry: sql(%s) error(%s)", sql, mysql_error(&dbconn));

View File

@ -21,6 +21,7 @@
#include <sys/stat.h>
#include <arpa/inet.h>
#include <glob.h>
#include <cinttypes>
#include "zm.h"
#include "zm_db.h"
@ -1245,9 +1246,9 @@ bool Monitor::Analyse() {
Info("%s: %d - Analysing at %.2f fps", name, image_count, new_fps);
if ( fps != new_fps ) {
fps = new_fps;
db_mutex.lock();
static char sql[ZM_SQL_SML_BUFSIZ];
snprintf(sql, sizeof(sql), "INSERT INTO Monitor_Status (MonitorId,AnalysisFPS) VALUES (%d, %.2lf) ON DUPLICATE KEY UPDATE AnalysisFPS = %.2lf", id, fps, fps);
db_mutex.lock();
if ( mysql_query(&dbconn, sql) ) {
Error("Can't run query: %s", mysql_error(&dbconn));
}

View File

@ -20,7 +20,7 @@
#define __STDC_FORMAT_MACROS 1
#include <inttypes.h>
#include <cinttypes>
#include <stdlib.h>
#include <string.h>

View File

@ -19,6 +19,7 @@
#include <sys/ipc.h>
#include <sys/msg.h>
#include <cinttypes>
#include "zm.h"
#include "zm_db.h"

View File

@ -87,6 +87,7 @@ Options for use with monitors:
*/
#include <getopt.h>
#include <cinttypes>
#include "zm.h"
#include "zm_db.h"

View File

@ -55,6 +55,22 @@ DocumentRoot /usr/share/zoneminder/www
qq`
ErrorLog $$opts{error_log}
Alias /zm/cache "/var/cache/zoneminder/cache"
Alias /cache "/var/cache/zoneminder/cache"
<Directory "/var/cache/zoneminder/cache">
Options -Indexes +FollowSymLinks
AllowOverride None
<IfModule mod_authz_core.c>
# Apache 2.4
Require all granted
</IfModule>
<IfModule !mod_authz_core.c>
# Apache 2.2
Order deny,allow
Allow from all
</IfModule>
</Directory>
ScriptAlias /zm/cgi-bin/ /usr/lib/zoneminder/cgi-bin/
ScriptAlias /cgi-bin/ /usr/lib/zoneminder/cgi-bin/
<Directory "/usr/lib/zoneminder/cgi-bin">
@ -67,7 +83,6 @@ ScriptAlias /cgi-bin/ /usr/lib/zoneminder/cgi-bin/
</Directory>
Alias /zm /usr/share/zoneminder/www
<Directory /usr/share/zoneminder/www>
php_flag register_globals off
Options +Indexes +FollowSymLinks
@ -81,6 +96,32 @@ Alias /zm /usr/share/zoneminder/www
Allow from all
</Directory>
# For better visibility, the following directives have been migrated from the
# default .htaccess files included with the CakePHP project.
# Parameters not set here are inherited from the parent directive above.
<Directory "/usr/share/zoneminder/www/api">
RewriteEngine on
RewriteRule ^$ app/webroot/ [L]
RewriteRule (.*) app/webroot/$1 [L]
RewriteBase /zm/api
</Directory>
<Directory "/usr/share/zoneminder/www/api/app">
RewriteEngine on
RewriteRule ^$ webroot/ [L]
RewriteRule (.*) webroot/$1 [L]
RewriteBase /zm/api
</Directory>
<Directory "/usr/share/zoneminder/www/api/app/webroot">
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
RewriteBase /zm/api
</Directory>
`;
if ( $$opts{protocol} eq 'https' ) {
$template .= qq`

View File

@ -105,10 +105,11 @@ commonprep () {
fi
# fix 32bit rpm builds
patch --dry-run --silent -f -p1 < utils/packpack/setarch.patch
if [ $? -eq 0 ]; then
patch -p1 < utils/packpack/setarch.patch
fi
# FIXME: breaks arm rpm builds
#patch --dry-run --silent -f -p1 < utils/packpack/setarch.patch
#if [ $? -eq 0 ]; then
# patch -p1 < utils/packpack/setarch.patch
#fi
# The rpm specfile requires we download each submodule as a tarball then manually move it into place
# Might as well do this for Debian as well, rather than git submodule init

View File

@ -1 +1 @@
1.31.42
1.31.44

View File

@ -6,14 +6,14 @@ class HostController extends AppController {
public $components = array('RequestHandler');
public function daemonCheck($daemon=false, $args=false) {
$string = Configure::read('ZM_PATH_BIN')."/zmdc.pl check";
$string = Configure::read('ZM_PATH_BIN').'/zmdc.pl check';
if ( $daemon ) {
$string .= " $daemon";
if ( $args )
$string .= " $args";
}
$result = exec( $string );
$result = preg_match( '/running/', $result );
$result = exec($string);
$result = preg_match('/running/', $result);
$this->set(array(
'result' => $result,
@ -31,10 +31,18 @@ class HostController extends AppController {
}
function getAuthHash() {
if ( $zmOptAuth == '1' ) {
require_once '../../../includes/auth.php';
$this->set(array(
'auth_hash'=> generateAuthHash( ZM_AUTH_HASH_IPS ),
'auth_hash' => generateAuthHash(ZM_AUTH_HASH_IPS),
'_serialize' => array('auth_hash')
) );
} else {
$this->set(array(
'auth_hash' => '',
'_serialize' => array('auth_hash')
) );
}
}
// If $mid is set, only return disk usage for that monitor

@ -1 +1 @@
Subproject commit 1351dde6b4c75b215099ae8bcf5a21d6c6e10298
Subproject commit 0bd63fb464957080ead342db58ca9e01532cf1ef

View File

@ -451,6 +451,11 @@ class Event {
$values[] = $this->{'Id'};
dbQuery( $sql, $values );
}
public function link_to($text=null) {
if ( !$text )
$text = $this->{'Id'};
return '<a href="?view=event&amp;eid='. $this->{'Id'}.'">'.$text.'</a>';
}
} # end class

View File

@ -13,7 +13,7 @@ class Storage {
if ( ! $row ) {
Error("Unable to load Storage record for Id=" . $IdOrRow );
}
} elseif ( is_array( $IdOrRow ) ) {
} else if ( is_array($IdOrRow) ) {
$row = $IdOrRow;
}
}

View File

@ -299,10 +299,12 @@ if ( isset($_REQUEST['object']) and $_REQUEST['object'] == 'Monitor' ) {
continue;
}
$Monitor = new Monitor( $mid );
if ( $Monitor->Type() != 'WebSite' ) {
$Monitor->zmaControl('stop');
$Monitor->zmcControl('stop');
}
$Monitor->save( $_REQUEST['newMonitor'] );
if ($Monitor->Function() != 'None' ) {
if ($Monitor->Function() != 'None' && $Monitor->Type() != 'WebSite' ) {
$Monitor->zmcControl('start');
if ( $Monitor->Enabled() ) {
$Monitor->zmaControl('start');
@ -330,7 +332,7 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
$monitor['Function'] = $newFunction;
$monitor['Enabled'] = $newEnabled;
if ( daemonCheck() ) {
if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
$restart = ($oldFunction == 'None') || ($newFunction == 'None') || ($newEnabled != $oldEnabled);
zmaControl( $monitor, 'stop' );
zmcControl( $monitor, $restart?'restart':'' );
@ -371,7 +373,7 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
} else {
dbQuery( 'INSERT INTO Zones SET MonitorId=?, '.implode( ', ', $changes ), array( $mid ) );
}
if ( daemonCheck() ) {
if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
if ( $_REQUEST['newZone']['Type'] == 'Privacy' ) {
zmaControl( $monitor, 'stop' );
zmcControl( $monitor, 'restart' );
@ -399,7 +401,7 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
}
}
if($changes>0) {
if ( daemonCheck() ) {
if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
zmaControl( $mid, 'restart' );
}
$refreshParent = true;
@ -424,7 +426,7 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
$deletedZid = 1;
}
if ( $deletedZid ) {
if ( daemonCheck() ) {
if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
if ( $zone['Type'] == 'Privacy' ) {
zmaControl( $mid, 'stop' );
zmcControl( $mid, 'restart' );
@ -492,8 +494,10 @@ if ( canEdit( 'Monitors' ) ) {
if ( $mid ) {
# If we change anything that changes the shared mem size, zma can complain. So let's stop first.
if ( $monitor['Type'] != 'WebSite' ) {
zmaControl( $monitor, 'stop' );
zmcControl( $monitor, 'stop' );
}
dbQuery( 'UPDATE Monitors SET '.implode( ', ', $changes ).' WHERE Id=?', array($mid) );
// Groups will be added below
if ( isset($changes['Name']) or isset($changes['StorageId']) ) {
@ -606,8 +610,10 @@ if ( canEdit( 'Monitors' ) ) {
$new_monitor = new Monitor($mid);
//fixDevices();
if ( $monitor['Type'] != 'WebSite' ) {
$new_monitor->zmcControl('start');
$new_monitor->zmaControl('start');
}
if ( $new_monitor->Controllable() ) {
require_once( 'control_functions.php' );

View File

@ -18,7 +18,7 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
function userLogin( $username, $password='', $passwordHashed=false ) {
function userLogin($username, $password='', $passwordHashed=false) {
global $user, $cookies;
$sql = 'SELECT * FROM Users WHERE Enabled=1';
@ -29,10 +29,10 @@ function userLogin( $username, $password='', $passwordHashed=false ) {
} else {
$sql .= ' AND Username=? AND Password=password(?)';
}
$sql_values = array( $username, $password );
$sql_values = array($username, $password);
} else {
$sql .= ' AND Username=?';
$sql_values = array( $username );
$sql_values = array($username);
}
session_start();
$_SESSION['username'] = $username;
@ -41,8 +41,8 @@ function userLogin( $username, $password='', $passwordHashed=false ) {
$_SESSION['password'] = $password;
}
$_SESSION['remoteAddr'] = $_SERVER['REMOTE_ADDR']; // To help prevent session hijacking
if ( $dbUser = dbFetchOne( $sql, NULL, $sql_values ) ) {
Info( "Login successful for user \"$username\"" );
if ( $dbUser = dbFetchOne($sql, NULL, $sql_values) ) {
Info("Login successful for user \"$username\"");
$_SESSION['user'] = $user = $dbUser;
unset($_SESSION['loginFailed']);
if ( ZM_AUTH_TYPE == 'builtin' ) {
@ -50,30 +50,30 @@ function userLogin( $username, $password='', $passwordHashed=false ) {
}
session_regenerate_id();
} else {
Warning( "Login denied for user \"$username\"" );
Warning("Login denied for user \"$username\"");
$_SESSION['loginFailed'] = true;
unset( $user );
unset($user);
}
session_write_close();
}
} # end function userLogin
function userLogout() {
global $user;
Info( 'User "'.$user['Username'].'" logged out' );
Info('User "'.$user['Username'].'" logged out');
session_start();
unset( $_SESSION['user'] );
unset( $user );
unset($_SESSION['user']);
unset($user);
session_destroy();
}
function getAuthUser( $auth ) {
function getAuthUser($auth) {
if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == 'hashed' && !empty($auth) ) {
$remoteAddr = '';
if ( ZM_AUTH_HASH_IPS ) {
$remoteAddr = $_SERVER['REMOTE_ADDR'];
if ( !$remoteAddr ) {
Error( "Can't determine remote address for authentication, using empty string" );
Error("Can't determine remote address for authentication, using empty string");
$remoteAddr = '';
}
}
@ -103,7 +103,7 @@ function getAuthUser( $auth ) {
return false;
} // end getAuthUser($auth)
function generateAuthHash( $useRemoteAddr ) {
function generateAuthHash($useRemoteAddr) {
if ( ZM_OPT_USE_AUTH and ZM_AUTH_RELAY == 'hashed' and isset($_SESSION['username']) and $_SESSION['passwordHash'] ) {
# regenerate a hash at half the liftetime of a hash, an hour is 3600 so half is 1800
$time = time();
@ -119,7 +119,7 @@ function generateAuthHash( $useRemoteAddr ) {
$authKey = ZM_AUTH_HASH_SECRET.$_SESSION['username'].$_SESSION['passwordHash'].$local_time[2].$local_time[3].$local_time[4].$local_time[5];
}
#Logger::Debug("Generated using hour:".$local_time[2] . ' mday:' . $local_time[3] . ' month:'.$local_time[4] . ' year: ' . $local_time[5] );
$auth = md5( $authKey );
$auth = md5($authKey);
session_start();
$_SESSION['AuthHash'] = $auth;
$_SESSION['AuthHashGeneratedAt'] = $time;
@ -135,22 +135,22 @@ function generateAuthHash( $useRemoteAddr ) {
return $auth;
}
function visibleMonitor( $mid ) {
function visibleMonitor($mid) {
global $user;
return( empty($user['MonitorIds']) || in_array( $mid, explode( ',', $user['MonitorIds'] ) ) );
return ( empty($user['MonitorIds']) || in_array($mid, explode(',', $user['MonitorIds'])) );
}
function canView( $area, $mid=false ) {
function canView($area, $mid=false) {
global $user;
return( ($user[$area] == 'View' || $user[$area] == 'Edit') && ( !$mid || visibleMonitor( $mid ) ) );
return ( ($user[$area] == 'View' || $user[$area] == 'Edit') && ( !$mid || visibleMonitor($mid) ) );
}
function canEdit( $area, $mid=false ) {
function canEdit($area, $mid=false) {
global $user;
return( $user[$area] == 'Edit' && ( !$mid || visibleMonitor( $mid ) ) );
return ( $user[$area] == 'Edit' && ( !$mid || visibleMonitor($mid) ));
}
?>

View File

@ -133,7 +133,7 @@ function dbQuery( $sql, $params=NULL ) {
}
} else {
if ( defined('ZM_DB_DEBUG') ) {
Logger::Debug("SQL: $sql values:" . $params?implode(',',$params):'' );
Logger::Debug("SQL: $sql values:" . ($params?implode(',',$params):'') );
}
$result = $dbConn->query($sql);
}

View File

@ -280,6 +280,27 @@ function getImageStill( $id, $src, $width, $height, $title='' ) {
return '<img id="'.$id.'" src="'.$src.'" alt="'.$title.'"'.(validInt($width)?' width="'.$width.'"':'').(validInt($height)?' height="'.$height.'"':'').'/>';
}
function getWebSiteUrl( $id, $src, $width, $height, $title='' ) {
# Prevent unsightly warnings when php cannot verify the ssl certificate
stream_context_set_default( [
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
],
]);
# The End User can turn off the following warning under Options -> Web
if ( ZM_WEB_XFRAME_WARN ) {
$header = get_headers($src, 1);
# If the target website has set X-Frame-Options, check it for "sameorigin" and warn the end user
if (array_key_exists('X-Frame-Options', $header)) {
$header = $header['X-Frame-Options'];
if ( stripos($header, 'sameorigin') === 0 )
Warning("Web site $src has X-Frame-Options set to sameorigin. An X-Frame-Options browser plugin is required to display this site.");
}
}
return '<object id="'.$id.'" data="'.$src.'" alt="'.$title.'" width="'.$width.'" height="'.$height.'"></object>';
}
function outputControlStill( $src, $width, $height, $monitor, $scale, $target ) {
?>
<form name="ctrlForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>" target="<?php echo $target ?>">
@ -495,11 +516,11 @@ function getFormChanges( $values, $newValues, $types=false, $columns=false ) {
switch( $types[$key] ) {
case 'set' :
{
if ( is_array( $newValues[$key] ) ) {
if ( join(',',$newValues[$key]) != $values[$key] ) {
if ( is_array($newValues[$key]) ) {
if ( (!isset($values[$key])) or ( join(',',$newValues[$key]) != $values[$key] ) ) {
$changes[$key] = "`$key` = ".dbEscape(join(',',$newValues[$key]));
}
} elseif ( $values[$key] ) {
} else if ( (!isset($values[$key])) or $values[$key] ) {
$changes[$key] = "`$key` = ''";
}
break;
@ -548,7 +569,7 @@ function getFormChanges( $values, $newValues, $types=false, $columns=false ) {
}
case 'raw' :
{
if ( $values[$key] != $value ) {
if ( (!isset($values[$key])) or ($values[$key] != $value) ) {
$changes[$key] = $key . ' = '.dbEscape($value);
}
break;
@ -1004,8 +1025,15 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&amp;') {
$filter['sql'] .= 'M.'.preg_replace('/^Monitor/', '', $terms[$i]['attr']);
break;
case 'ServerId':
case 'MonitorServerId':
$filter['sql'] .= 'M.ServerId';
break;
case 'StorageServerId':
$filter['sql'] .= 'S.ServerId';
break;
case 'FilterServerId':
$filter['sql'] .= ZM_SERVER_ID;
break;
# Unspecified start or end, so assume start, this is to support legacy filters
case 'DateTime':
$filter['sql'] .= 'E.StartTime';
@ -1100,6 +1128,9 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&amp;') {
case 'Notes':
$value = dbEscape($value);
break;
case 'MonitorServerId':
case 'FilterServerId':
case 'StorageServerId':
case 'ServerId':
if ( $value == 'ZM_SERVER_ID' ) {
$value = ZM_SERVER_ID;
@ -2128,8 +2159,14 @@ function getStreamHTML( $monitor, $options = array() ) {
$options['buffer'] = $monitor->StreamReplayBuffer();
//Warning("width: " . $options['width'] . ' height: ' . $options['height']. ' scale: ' . $options['scale'] );
if ( $monitor->Type() == "WebSite" ) {
return getWebSiteUrl( 'liveStream'.$monitor->Id(), $monitor->Path(),
( isset($options['width']) ? $options['width'] : NULL ),
( isset($options['height']) ? $options['height'] : NULL ),
$monitor->Name()
);
//FIXME, the width and height of the image need to be scaled.
if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
} else if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
$streamSrc = $monitor->getStreamSrc( array(
'mode'=>'mpeg',
'scale'=>(isset($options['scale'])?$options['scale']:100),

View File

@ -130,7 +130,9 @@ $SLANG = array(
'AttrMonitorId' => 'Monitor Id',
'AttrMonitorName' => 'Monitor Name',
'AttrStorageArea' => 'Storage Area',
'AttrServer' => 'Server',
'AttrFilterServer' => 'Server Filter is Running On',
'AttrMonitorServer' => 'Server Monitor is Running On',
'AttrStorageServer' => 'Server Hosting Storage',
'AttrStateId' => 'Run State',
'AttrName' => 'Name',
'AttrNotes' => 'Notes',
@ -174,8 +176,10 @@ $SLANG = array(
'BadSectionLength' => 'Section length must be an integer of 30 or more',
'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string',
'BadStreamReplayBuffer' => 'Stream replay buffer must be an integer of zero or more',
'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"',
'BadWarmupCount' => 'Warmup frames must be an integer of zero or more',
'BadWebColour' => 'Web colour must be a valid web colour string',
'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.',
'BadWidth' => 'Width must be set to a valid value',
'Bandwidth' => 'Bandwidth',
'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing
@ -768,6 +772,7 @@ $SLANG = array(
'Watch' => 'Watch',
'WebColour' => 'Web Colour',
'Web' => 'Web',
'WebSiteUrl' => 'Website URL',
'Week' => 'Week',
'WhiteBalance' => 'White Balance',
'White' => 'White',

View File

@ -244,7 +244,7 @@ function getNavBarHTML($reload = null) {
if ( logToDatabase() > Logger::NOLOG ) {
if ( ! ZM_RUN_AUDIT ) {
# zmaudit can clean the logs, but if we aren't running it, then we should clecan them regularly
dbQuery('DELETE FROM Logs WHERE TimeKey < unix_timestamp( NOW() - interval '.ZM_LOG_DATABASE_LIMIT.')');
dbQuery('DELETE FROM Logs WHERE TimeKey < unix_timestamp( NOW() - interval '.ZM_LOG_DATABASE_LIMIT.') LIMIT 100');
}
echo makePopupLink( '?view=log', 'zmLog', 'log', '<span class="'.logState().'">'.translate('Log').'</span>' );
}

View File

@ -111,6 +111,9 @@ $status_counts = array();
for ( $i = 0; $i < count($displayMonitors); $i++ ) {
$monitor = &$displayMonitors[$i];
if ( ! $monitor['Status'] ) {
if ( $monitor['Type'] == 'WebSite' )
$monitor['Status'] = 'Running';
else
$monitor['Status'] = 'NotRunning';
}
if ( !isset($status_counts[$monitor['Status']]) )
@ -238,7 +241,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
?>
<tr id="<?php echo 'monitor_id-'.$monitor['Id'] ?>" title="<?php echo $monitor['Id'] ?>">
<?php
if ( (!$monitor['Status']) or ($monitor['Status'] == 'NotRunning') ) {
if ( (!$monitor['Status'] || $monitor['Status'] == 'NotRunning') && $monitor['Type']!='WebSite' ) {
$source_class = 'errorText';
} else {
if ( $monitor['CaptureFPS'] == '0.00' ) {
@ -256,7 +259,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
if ( !$monitor['Enabled'] )
$fclass .= ' disabledText';
$scale = max( reScale( SCALE_BASE, $monitor['DefaultScale'], ZM_WEB_DEFAULT_SCALE ), SCALE_BASE );
$stream_available = canView('Stream') && $monitor['CaptureFPS'] && $monitor['Function'] != 'None';
$stream_available = canView('Stream') and $monitor['Type']=='WebSite' or ($monitor['CaptureFPS'] && $monitor['Function'] != 'None');
$dot_class=$source_class;
if ( $fclass != 'infoText' ) $dot_class=$fclass;
@ -313,7 +316,7 @@ if ( $fclass != 'infoText' ) $dot_class=$fclass;
}
} elseif ( $monitor['Type'] == 'File' || $monitor['Type'] == 'cURL' ) {
$source = preg_replace( '/^.*\//', '', $monitor['Path'] );
} elseif ( $monitor['Type'] == 'Ffmpeg' || $monitor['Type'] == 'Libvlc' ) {
} elseif ( $monitor['Type'] == 'Ffmpeg' || $monitor['Type'] == 'Libvlc' || $monitor['Type'] == 'WebSite' ) {
$url_parts = parse_url( $monitor['Path'] );
unset($url_parts['user']);
unset($url_parts['pass']);

View File

@ -101,7 +101,10 @@ $attrTypes = array(
'DiskSpace' => translate('AttrDiskSpace'),
'SystemLoad' => translate('AttrSystemLoad'),
'StorageId' => translate('AttrStorageArea'),
'ServerId' => translate('AttrServer'),
'ServerId' => translate('AttrMonitorServer'),
'FilterServerId' => translate('AttrFilterServer'),
'MonitorServerId' => translate('AttrMonitorServer'),
'StorageServerId' => translate('AttrStorageServer'),
'StateId' => translate('AttrStateId'),
);
@ -268,7 +271,7 @@ for ( $i=0; $i < count($terms); $i++ ) {
<td><?php echo htmlSelect( "filter[Query][terms][$i][op]", $opTypes, $term['op'] ); ?></td>
<td><?php echo htmlSelect( "filter[Query][terms][$i][val]", $monitors, $term['val'] ); ?></td>
<?php
} elseif ( $term['attr'] == 'ServerId' ) {
} elseif ( $term['attr'] == 'ServerId' || $term['attr'] == 'MonitorServerId' || $term['attr'] == 'StorageServerId' || $term['attr'] == 'FilterServerId' ) {
?>
<td><?php echo htmlSelect( "filter[Query][terms][$i][op]", $opTypes, $term['op'] ); ?></td>
<td><?php echo htmlSelect( "filter[Query][terms][$i][val]", $servers, $term['val'] ); ?></td>

View File

@ -142,7 +142,9 @@ function parseRows (rows) {
inputTds.eq(6).find(':input[value="-"]').prop('disabled', false);
}
if (inputTds.eq(2).children().val() == "Archived") { //Archived types
var attr = inputTds.eq(2).children().val();
if ( attr == "Archived") { //Archived types
inputTds.eq(3).html('equal to<input type="hidden" name="filter[Query][terms][' + rowNum + '][op]" value="=">');
let archiveSelect = $j('<select></select>').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]');
for (let i = 0; i < archiveTypes.length; i++) {
@ -151,7 +153,7 @@ function parseRows (rows) {
let archiveVal = inputTds.eq(4).children().val();
inputTds.eq(4).html(archiveSelect).children().val(archiveVal).chosen({width: "101%"});
} else if (inputTds.eq(2).children().val().indexOf('Weekday') >= 0) { //Weekday selection
} else if ( attr.indexOf('Weekday') >= 0 ) { //Weekday selection
let weekdaySelect = $j('<select></select>').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]');
for (let i = 0; i < weekdays.length; i++) {
weekdaySelect.append('<option value="' + i + '">' + weekdays[i] + '</option>');
@ -159,7 +161,7 @@ function parseRows (rows) {
let weekdayVal = inputTds.eq(4).children().val();
inputTds.eq(4).html(weekdaySelect).children().val(weekdayVal).chosen({width: "101%"});
} else if (inputTds.eq(2).children().val() == 'StateId') { //Run state
} else if ( attr == 'StateId' ) { //Run state
let stateSelect = $j('<select></select>').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]');
for (let key in states) {
stateSelect.append('<option value="' + key + '">' + states[key] + '</option>');
@ -167,8 +169,7 @@ function parseRows (rows) {
let stateVal = inputTds.eq(4).children().val();
inputTds.eq(4).html(stateSelect).children().val(stateVal).chosen({width: "101%"});
} else if (inputTds.eq(2).children().val() == 'ServerId') { //Select Server
} else if ( attr == 'ServerId' || attr == 'MonitorServerId' || attr == 'StorageServerId' || attr == 'FilterServerId' ) { //Select Server
let serverSelect = $j('<select></select>').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]');
for (let key in servers) {
serverSelect.append('<option value="' + key + '">' + servers[key] + '</option>');
@ -176,21 +177,15 @@ function parseRows (rows) {
let serverVal = inputTds.eq(4).children().val();
inputTds.eq(4).html(serverSelect).children().val(serverVal).chosen({width: "101%"});
} else if (inputTds.eq(2).children().val() == 'StorageId') { //Choose by storagearea
} else if ( attr == 'StorageId' ) { //Choose by storagearea
let storageSelect = $j('<select></select>').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]');
for ( key in storageareas ) {
console.log(key + ' ' + storageareas[key]);
storageSelect.append('<option value="' + key + '">' + storageareas[key] + '</option>');
}
/*
for (let i=0; i < storageareas.length; i++) {
storageSelect.append('<option value="' + i + '">' + storageareas[i] + '</option>');
}
*/
let storageVal = inputTds.eq(4).children().val();
inputTds.eq(4).html(storageSelect).children().val(storageVal).chosen({width: "101%"});
} else if (inputTds.eq(2).children().val() == 'MonitorName') { //Monitor names
} else if ( attr == 'MonitorName' ) { //Monitor names
let monitorSelect = $j('<select></select>').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]');
for (let key in monitors) {
monitorSelect.append('<option value="' + key + '">' + monitors[key] + '</option>');
@ -208,15 +203,15 @@ console.log(key + ' ' + storageareas[key]);
let textVal = inputTds.eq(4).children().val();
inputTds.eq(4).html(textInput).children().val(textVal);
}
if (inputTds.eq(2).children().val().endsWith('DateTime')) { //Start/End DateTime
if ( attr.endsWith('DateTime') ) { //Start/End DateTime
inputTds.eq(4).children().datetimepicker({timeFormat: "HH:mm:ss", dateFormat: "yy-mm-dd", maxDate: 0, constrainInput: false});
} else if (inputTds.eq(2).children().val().endsWith('Date')) { //Start/End Date
} else if ( attr.endsWith('Date') ) { //Start/End Date
inputTds.eq(4).children().datepicker({dateFormat: "yy-mm-dd", maxDate: 0, constrainInput: false});
} else if (inputTds.eq(2).children().val().endsWith('Time')) { //Start/End Time
} else if ( attr.endsWith('Time')) { //Start/End Time
inputTds.eq(4).children().timepicker({timeFormat: "HH:mm:ss", constrainInput: false});
}
let attr = inputTds.find("[name$='attr\\]']") // Set attr list id and name
attr = inputTds.find("[name$='attr\\]']") // Set attr list id and name
let term = attr.attr('name').split(/[[\]]{1,2}/);
term.length--;
term.shift();

View File

@ -54,14 +54,6 @@ function validateForm( form ) {
else if ( monitorNames[form.elements['newMonitor[Name]'].value] )
errors[errors.length] = "<?php echo translate('DuplicateMonitorName') ?>";
if ( form.elements['newMonitor[AnalysisFPSLimit]'].value && !(parseFloat(form.elements['newMonitor[AnalysisFPSLimit]'].value) > 0 ) )
errors[errors.length] = "<?php echo translate('BadAnalysisFPS') ?>";
if ( form.elements['newMonitor[MaxFPS]'].value && !(parseFloat(form.elements['newMonitor[MaxFPS]'].value) > 0 ) )
errors[errors.length] = "<?php echo translate('BadMaxFPS') ?>";
if ( form.elements['newMonitor[AlarmMaxFPS]'].value && !(parseFloat(form.elements['newMonitor[AlarmMaxFPS]'].value) > 0 ) )
errors[errors.length] = "<?php echo translate('BadAlarmMaxFPS') ?>";
if ( !form.elements['newMonitor[RefBlendPerc]'].value || (parseInt(form.elements['newMonitor[RefBlendPerc]'].value) > 100 ) || (parseInt(form.elements['newMonitor[RefBlendPerc]'].value) < 0 ) )
errors[errors.length] = "<?php echo translate('BadRefBlendPerc') ?>";
if ( form.elements['newMonitor[Type]'].value == 'Local' ) {
if ( !form.elements['newMonitor[Palette]'].value || !form.elements['newMonitor[Palette]'].value.match( /^\d+$/ ) )
errors[errors.length] = "<?php echo translate('BadPalette') ?>";
@ -81,7 +73,24 @@ function validateForm( form ) {
} else if ( form.elements['newMonitor[Type]'].value == 'File' ) {
if ( !form.elements['newMonitor[Path]'].value )
errors[errors.length] = "<?php echo translate('BadPath') ?>";
} else if ( form.elements['newMonitor[Type]'].value == 'WebSite' ) {
if ( form.elements['newMonitor[Function]'].value != 'Monitor' && form.elements['newMonitor[Function]'].value != 'None')
errors[errors.length] = "<?php echo translate('BadSourceType') ?>";
if ( form.elements['newMonitor[Path]'].value.search(/^https?:\/\//i) )
errors[errors.length] = "<?php echo translate('BadWebSitePath') ?>";
}
if ( form.elements['newMonitor[Type]'].value != 'WebSite' ) {
if ( form.elements['newMonitor[AnalysisFPSLimit]'].value && !(parseFloat(form.elements['newMonitor[AnalysisFPSLimit]'].value) > 0 ) )
errors[errors.length] = "<?php echo translate('BadAnalysisFPS') ?>";
if ( form.elements['newMonitor[MaxFPS]'].value && !(parseFloat(form.elements['newMonitor[MaxFPS]'].value) > 0 ) )
errors[errors.length] = "<?php echo translate('BadMaxFPS') ?>";
if ( form.elements['newMonitor[AlarmMaxFPS]'].value && !(parseFloat(form.elements['newMonitor[AlarmMaxFPS]'].value) > 0 ) )
errors[errors.length] = "<?php echo translate('BadAlarmMaxFPS') ?>";
if ( !form.elements['newMonitor[RefBlendPerc]'].value || (parseInt(form.elements['newMonitor[RefBlendPerc]'].value) > 100 ) || (parseInt(form.elements['newMonitor[RefBlendPerc]'].value) < 0 ) )
errors[errors.length] = "<?php echo translate('BadRefBlendPerc') ?>";
if ( !form.elements['newMonitor[Colours]'].value || (parseInt(form.elements['newMonitor[Colours]'].value) != 1 && parseInt(form.elements['newMonitor[Colours]'].value) != 3 && parseInt(form.elements['newMonitor[Colours]'].value) != 4 ) )
errors[errors.length] = "<?php echo translate('BadColours') ?>";
if ( !form.elements['newMonitor[Width]'].value || !(parseInt(form.elements['newMonitor[Width]'].value) > 0 ) )
@ -120,6 +129,8 @@ function validateForm( form ) {
if ( !form.elements['newMonitor[WebColour]'].value || !form.elements['newMonitor[WebColour]'].value.match( /^[#0-9a-zA-Z]+$/ ) )
errors[errors.length] = "<?php echo translate('BadWebColour') ?>";
}
if ( errors.length ) {
alert( errors.join( "\n" ) );
return( false );

View File

@ -12,6 +12,15 @@ function Monitor( monitorData ) {
if ( auth_hash )
this.streamCmdParms += '&auth='+auth_hash;
this.streamCmdTimer = null;
this.type = monitorData.type;
this.refresh = monitorData.refresh;
this.start = function( delay ) {
if ( this.streamCmdQuery )
this.streamCmdTimer = this.streamCmdQuery.delay( delay, this );
else
console.log("No streamCmdQuery");
};
this.setStateClass = function( element, stateClass ) {
if ( !element.hasClass( stateClass ) ) {
@ -68,7 +77,7 @@ function Monitor( monitorData ) {
else
stateClass = "idle";
if ( !COMPACT_MONTAGE ) {
if ( (!COMPACT_MONTAGE) && (this.type != 'WebSite') ) {
$('fpsValue'+this.id).set( 'text', this.status.fps );
$('stateValue'+this.id).set( 'text', stateStrings[this.alarmState] );
this.setStateClass( $('monitorState'+this.id), stateClass );
@ -137,9 +146,12 @@ function Monitor( monitorData ) {
this.streamCmdReq.cancel();
}
//console.log("Starting CmdQuery for " + this.connKey );
if ( this.type != 'WebSite' ) {
this.streamCmdReq.send( this.streamCmdParms+"&command="+CMD_QUERY );
}
};
if ( this.type != 'WebSite' ) {
this.streamCmdReq = new Request.JSON( {
url: this.server_url,
method: 'get',
@ -150,9 +162,10 @@ function Monitor( monitorData ) {
onFailure: this.onFailure.bind(this),
link: 'cancel'
} );
console.log("queueing for " + this.id + " " + this.connKey );
requestQueue.addRequest( "cmdReq"+this.id, this.streamCmdReq );
}
}
function selectLayout( element ) {
@ -378,15 +391,26 @@ function cancel_layout(button) {
selectLayout('#zmMontageLayout');
}
function reloadWebSite(ndx) {
document.getElementById('imageFeed'+ndx).innerHTML = document.getElementById('imageFeed'+ndx).innerHTML;
}
var monitors = new Array();
function initPage() {
console.log("initPage");
for ( var i = 0; i < monitorData.length; i++ ) {
monitors[i] = new Monitor(monitorData[i]);
var delay = Math.round( (Math.random()+0.5)*statusRefreshTimeout );
var interval = monitors[i].refresh;
monitors[i].start( delay );
if ( monitors[i].type == 'WebSite' && interval > 0 ) {
setInterval(reloadWebSite, interval*1000, i);
}
}
selectLayout('#zmMontageLayout');
for ( var i = 0; i < monitorData.length; i++ ) {
if ( monitors[i].type == 'WebSite' )
continue;
var delay = Math.round( (Math.random()+0.75)*statusRefreshTimeout );
console.log("Delay for monitor " + monitorData[i].id + " is " + delay );
monitors[i].streamCmdQuery.delay( delay, monitors[i] );

View File

@ -36,7 +36,9 @@ monitorData[monitorData.length] = {
'width': <?php echo $monitor->Width() ?>,
'height':<?php echo $monitor->Height() ?>,
'server_url': '<?php echo $monitor->Server()->Url().$_SERVER['PHP_SELF'] ?>',
'onclick': function(){createPopup( '?view=watch&mid=<?php echo $monitor->Id() ?>', 'zmWatch<?php echo $monitor->Id() ?>', 'watch', <?php echo reScale( $monitor->Width(), $monitor->PopupScale() ); ?>, <?php echo reScale( $monitor->Height(), $monitor->PopupScale() ); ?> );}
'onclick': function(){createPopup( '?view=watch&mid=<?php echo $monitor->Id() ?>', 'zmWatch<?php echo $monitor->Id() ?>', 'watch', <?php echo reScale( $monitor->Width(), $monitor->PopupScale() ); ?>, <?php echo reScale( $monitor->Height(), $monitor->PopupScale() ); ?> );},
'type': '<?php echo $monitor->Type() ?>',
'refresh': '<?php echo $monitor->Refresh() ?>'
};
<?php
} // end foreach monitor

View File

@ -105,18 +105,20 @@ function setAlarmState( currentAlarmState ) {
lastAlarmState = alarmState;
}
var streamCmdParms = "view=request&request=stream&connkey="+connKey;
if ( auth_hash )
if ( monitorType != 'WebSite' ) {
var streamCmdParms = "view=request&request=stream&connkey="+connKey;
if ( auth_hash )
streamCmdParms += '&auth='+auth_hash;
var streamCmdReq = new Request.JSON( {
var streamCmdReq = new Request.JSON( {
url: monitorUrl+thisUrl,
method: 'get',
timeout: AJAX_TIMEOUT,
link: 'chain',
onSuccess: getStreamCmdResponse,
onFailure: getStreamCmdFailure
} );
var streamCmdTimer = null;
} );
var streamCmdTimer = null;
}
var streamStatus;
@ -350,11 +352,13 @@ function streamCmdQuery() {
streamCmdReq.send( streamCmdParms+"&command="+CMD_QUERY );
}
var statusCmdParms = "view=request&request=status&entity=monitor&id="+monitorId+"&element[]=Status&element[]=FrameRate";
if ( auth_hash )
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+thisUrl, method: 'get', data: statusCmdParms, timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getStatusCmdResponse } );
var statusCmdTimer = null;
var statusCmdReq = new Request.JSON( { url: monitorUrl+thisUrl, method: 'get', data: statusCmdParms, timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getStatusCmdResponse } );
var statusCmdTimer = null;
}
function getStatusCmdResponse( respObj, respText ) {
watchdogOk("status");
@ -377,18 +381,20 @@ function statusCmdQuery() {
statusCmdReq.send();
}
var alarmCmdParms = "view=request&request=alarm&id="+monitorId;
if ( auth_hash )
if ( monitorType != 'WebSite' ) {
var alarmCmdParms = "view=request&request=alarm&id="+monitorId;
if ( auth_hash )
alarmCmdParms += '&auth='+auth_hash;
var alarmCmdReq = new Request.JSON( {
var alarmCmdReq = new Request.JSON( {
url: monitorUrl+thisUrl,
method: 'post',
timeout: AJAX_TIMEOUT,
link: 'cancel',
onSuccess: getAlarmCmdResponse,
onTimeout: streamCmdQuery
} );
var alarmCmdFirst = true;
} );
var alarmCmdFirst = true;
}
function getAlarmCmdResponse( respObj, respText ) {
checkStreamForErrors("getAlarmCmdResponse", respObj);
@ -428,12 +434,14 @@ function deleteEvent( event, eventId ) {
event.stop();
}
var eventCmdParms = "view=request&request=status&entity=events&id="+monitorId+"&count="+maxDisplayEvents+"&sort=Id%20desc";
if ( auth_hash )
if ( monitorType != 'WebSite' ) {
var eventCmdParms = "view=request&request=status&entity=events&id="+monitorId+"&count="+maxDisplayEvents+"&sort=Id%20desc";
if ( auth_hash )
eventCmdParms += '&auth='+auth_hash;
var eventCmdReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, data: eventCmdParms, link: 'cancel', onSuccess: getEventCmdResponse, onTimeout: eventCmdQuery } );
var eventCmdTimer = null;
var eventCmdFirst = true;
var eventCmdReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, data: eventCmdParms, link: 'cancel', onSuccess: getEventCmdResponse, onTimeout: eventCmdQuery } );
var eventCmdTimer = null;
var eventCmdFirst = true;
}
function highlightRow( row ) {
$(row).toggleClass( 'highlight' );
@ -536,10 +544,12 @@ function eventCmdQuery() {
eventCmdReq.send();
}
var controlParms = "view=request&request=control&id="+monitorId;
if ( auth_hash )
if ( monitorType != 'WebSite' ) {
var controlParms = "view=request&request=control&id="+monitorId;
if ( auth_hash )
controlParms += '&auth='+auth_hash;
var controlReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getControlResponse } );
var controlReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getControlResponse } );
}
function getControlResponse( respObj, respText ) {
if ( !respObj )
@ -652,6 +662,7 @@ function watchdogOk( type ) {
}
function initPage() {
if ( monitorType != 'WebSite' ) {
if ( streamMode == "single" ) {
statusCmdTimer = statusCmdQuery.delay( (Math.random()+0.1)*statusRefreshTimeout );
watchdogCheck.pass('status').periodical(statusRefreshTimeout*2);
@ -680,6 +691,9 @@ function initPage() {
if (window.history.length == 1) {
$j('#closeControl').html('');
}
} else if ( monitorRefresh > 0 ) {
var myReload = setInterval(reloadWebSite, monitorRefresh*1000);
}
}
// Kick everything off

View File

@ -49,6 +49,8 @@ var monitorId = <?php echo $monitor->Id() ?>;
var monitorWidth = <?php echo $monitor->Width() ?>;
var monitorHeight = <?php echo $monitor->Height() ?>;
var monitorUrl = '<?php echo ( $monitor->Server()->Url() . ( ZM_MIN_STREAMING_PORT ? ':'. (ZM_MIN_STREAMING_PORT+$monitor->Id()) : '' ) ) ?>';
var monitorType = '<?php echo ( $monitor->Type() ) ?>';
var monitorRefresh = '<?php echo ( $monitor->Refresh() ) ?>';
var scale = '<?php echo $scale ?>';

View File

@ -26,23 +26,6 @@ if ( !canView( 'Monitors' ) ) {
return;
}
$tabs = array();
$tabs['general'] = translate('General');
$tabs['source'] = translate('Source');
$tabs['storage'] = translate('Storage');
$tabs['timestamp'] = translate('Timestamp');
$tabs['buffers'] = translate('Buffers');
if ( ZM_OPT_CONTROL && canView( 'Control' ) )
$tabs['control'] = translate('Control');
if ( ZM_OPT_X10 )
$tabs['x10'] = translate('X10');
$tabs['misc'] = translate('Misc');
if ( isset($_REQUEST['tab']) )
$tab = validHtmlStr($_REQUEST['tab']);
else
$tab = 'general';
$Server = null;
if ( defined( 'ZM_SERVER_ID' ) ) {
$Server = dbFetchOne( 'SELECT * FROM Servers WHERE Id=?', NULL, array( ZM_SERVER_ID ) );
@ -142,6 +125,7 @@ if ( ! $monitor ) {
'V4LCapturesPerFrame' => 1,
'ServerId' => 'auto',
'StorageId' => '1',
'Refresh' => '',
) );
} # end if $_REQUEST['dupID']
} # end if $_REQUEST['mid']
@ -212,6 +196,7 @@ $sourceTypes = array(
'Ffmpeg' => translate('Ffmpeg'),
'Libvlc' => translate('Libvlc'),
'cURL' => 'cURL (HTTP(S) only)',
'WebSite'=> 'Web Site',
'NVSocket' => translate('NVSocket')
);
if ( !ZM_HAS_V4L )
@ -507,6 +492,25 @@ if ( canEdit( 'Monitors' ) ) {
<div id="content">
<ul class="tabList">
<?php
$tabs = array();
$tabs['general'] = translate('General');
$tabs['source'] = translate('Source');
if ( $monitor->Type() != 'WebSite' ) {
$tabs['storage'] = translate('Storage');
$tabs['timestamp'] = translate('Timestamp');
$tabs['buffers'] = translate('Buffers');
if ( ZM_OPT_CONTROL && canView( 'Control' ) )
$tabs['control'] = translate('Control');
if ( ZM_OPT_X10 )
$tabs['x10'] = translate('X10');
$tabs['misc'] = translate('Misc');
}
if ( isset($_REQUEST['tab']) )
$tab = validHtmlStr($_REQUEST['tab']);
else
$tab = 'general';
foreach ( $tabs as $name=>$value ) {
if ( $tab == $name ) {
?>
@ -583,7 +587,7 @@ if ( $tab != 'source' || ($monitor->Type()!= 'Ffmpeg' && $monitor->Type()!= 'Lib
<input type="hidden" name="newMonitor[Options]" value="<?php echo validHtmlStr($monitor->Options()) ?>"/>
<?php
}
if ( $tab != 'source' || ($monitor->Type()!= 'Remote' && $monitor->Type()!= 'File' && $monitor->Type()!= 'Ffmpeg' && $monitor->Type()!= 'Libvlc' && $monitor->Type()!= 'cURL') ) {
if ( $tab != 'source' || ($monitor->Type()!= 'Remote' && $monitor->Type()!= 'File' && $monitor->Type()!= 'Ffmpeg' && $monitor->Type()!= 'Libvlc' && $monitor->Type()!= 'cURL' && $monitor->Type() != 'WebSite') ) {
?>
<input type="hidden" name="newMonitor[Path]" value="<?php echo validHtmlStr($monitor->Path()) ?>"/>
<input type="hidden" name="newMonitor[User]" value="<?php echo validHtmlStr($monitor->User()) ?>"/>
@ -713,6 +717,9 @@ switch ( $tab ) {
?>
</select></td></tr>
<tr><td><?php echo translate('Enabled') ?></td><td><input type="checkbox" name="newMonitor[Enabled]" value="1"<?php if ( $monitor->Enabled() ) { ?> checked="checked"<?php } ?>/></td></tr>
<?php
if ( $monitor->Type != 'WebSite' ) {
?>
<tr>
<td><?php echo translate('LinkedMonitors') ?></td>
<td>
@ -802,6 +809,7 @@ echo htmlOptions(Group::get_dropdown_options( ), $monitor->GroupIds() );
?>
</td></tr>
<?php
}
break;
}
case 'source' :
@ -868,6 +876,13 @@ include('_monitor_source_nvsocket.php');
<tr><td><?php echo 'URL' ?></td><td><input type="text" name="newMonitor[Path]" value="<?php echo validHtmlStr($monitor->Path()) ?>" size="36"/></td></tr>
<tr><td><?php echo 'Username' ?></td><td><input type="text" name="newMonitor[User]" value="<?php echo validHtmlStr($monitor->User()) ?>" size="12"/></td></tr>
<tr><td><?php echo 'Password' ?></td><td><input type="text" name="newMonitor[Pass]" value="<?php echo validHtmlStr($monitor->Pass()) ?>" size="12"/></td></tr>
<?php
} elseif ( $monitor->Type() == 'WebSite' ) {
?>
<tr><td><?php echo translate('WebSiteUrl') ?></td><td><input type="text" name="newMonitor[Path]" value="<?php echo validHtmlStr($monitor->Path()) ?>" size="36"/></td></tr>
<tr><td><?php echo translate('Width') ?> (<?php echo translate('Pixels') ?>)</td><td><input type="text" name="newMonitor[Width]" value="<?php echo validHtmlStr($monitor->Width()) ?>" size="4";"/></td></tr>
<tr><td><?php echo translate('Height') ?> (<?php echo translate('Pixels') ?>)</td><td><input type="text" name="newMonitor[Height]" value="<?php echo validHtmlStr($monitor->Height()) ?>" size="4";"/></td></tr>
<tr><td><?php echo 'Web Site Refresh (Optional)' ?></td><td><input type="number" name="newMonitor[Refresh]" value="<?php echo $monitor->Refresh()?>"/></td></tr>
<?php
} elseif ( $monitor->Type() == 'Ffmpeg' || $monitor->Type() == 'Libvlc' ) {
?>
@ -876,7 +891,7 @@ include('_monitor_source_nvsocket.php');
<tr><td><?php echo translate('Options') ?>&nbsp;(<?php echo makePopupLink( '?view=optionhelp&amp;option=OPTIONS_'.strtoupper($monitor->Type()), 'zmOptionHelp', 'optionhelp', '?' ) ?>)</td><td><input type="text" name="newMonitor[Options]" value="<?php echo validHtmlStr($monitor->Options()) ?>" size="36"/></td></tr>
<?php
}
if ( $monitor->Type() != 'NVSocket' ) {
if ( $monitor->Type() != 'NVSocket' && $monitor->Type() != 'WebSite' ) {
?>
<tr><td><?php echo translate('TargetColorspace') ?></td><td><?php echo htmlSelect('newMonitor[Colours]', $Colours, $monitor->Colours() ); ?>
</td></tr>
@ -890,7 +905,7 @@ if ( $monitor->Type() == 'Local' ) {
?>
<tr><td><?php echo translate('Deinterlacing') ?></td><td><select name="newMonitor[Deinterlacing]"><?php foreach ( $deinterlaceopts_v4l2 as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->Deinterlacing()) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
<?php
} else {
} else if ( $monitor->Type() != 'WebSite' ) {
?>
<tr><td><?php echo translate('Deinterlacing') ?></td><td><select name="newMonitor[Deinterlacing]"><?php foreach ( $deinterlaceopts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->Deinterlacing()) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
<?php

View File

@ -204,7 +204,11 @@ foreach ( $monitors as $monitor ) {
}
}
if ( $monitor->Type() == "WebSite" ) {
echo getWebSiteUrl( 'liveStream'.$monitor->Id(), $monitor->Path(), reScale( $monitor->Width(), $scale ), reScale( $monitor->Height(), $scale ), $monitor->Name() );
} else {
echo getStreamHTML( $monitor, $monitor_options );
}
if ( $showZones ) {
$height = null;
$width = null;
@ -255,7 +259,7 @@ foreach ( $monitors as $monitor ) {
<?php } # end if showZones ?>
</div>
<?php
if ( !ZM_WEB_COMPACT_MONTAGE ) {
if ( (!ZM_WEB_COMPACT_MONTAGE) && ($monitor->Type() != 'WebSite') ) {
?>
<div id="monitorState<?php echo $monitor->Id() ?>" class="monitorState idle"><?php echo translate('State') ?>:&nbsp;<span id="stateValue<?php echo $monitor->Id() ?>"></span>&nbsp;-&nbsp;<span id="fpsValue<?php echo $monitor->Id() ?>"></span>&nbsp;fps</div>
<?php

View File

@ -207,7 +207,7 @@ $frameSql .= ' ORDER BY Id DESC';
$monitors = array();
foreach( $displayMonitors as $row ) {
if ( $row['Function'] == 'None' )
if ( $row['Function'] == 'None' || $row['Type'] == 'WebSite' )
continue;
$Monitor = new Monitor( $row );
$monitors[] = $Monitor;

View File

@ -234,7 +234,7 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
<td class="colMemory
<?php if ( $row['FreeMem']/$row['TotalMem'] < .1 ) { echo 'danger'; } ?>"><?php echo makePopupLink( '?view=server&amp;id='.$row['Id'], 'zmServer', 'server', human_filesize($row['FreeMem']) . ' / ' . human_filesize($row['TotalMem']), $canEdit ) ?></td>
<td class="colSwap
<?php if ( $row['FreeSwap']/$row['TotalSwap'] < .1 ) { echo 'danger'; } ?>"><?php echo makePopupLink( '?view=server&amp;id='.$row['Id'], 'zmServer', 'server', human_filesize($row['FreeSwap']) . ' / ' . human_filesize($row['TotalSwap']) , $canEdit ) ?></td>
<?php if ( (!$row['TotalSwap']) or ($row['FreeSwap']/$row['TotalSwap'] < .1) ) { echo 'danger'; } ?>"><?php echo makePopupLink( '?view=server&amp;id='.$row['Id'], 'zmServer', 'server', human_filesize($row['FreeSwap']) . ' / ' . human_filesize($row['TotalSwap']) , $canEdit ) ?></td>
<td class="colStats"><?php echo makePopupLink( '?view=server&amp;id='.$row['Id'], 'zmServer', 'server', $row['zmstats'] ? 'yes' : 'no', $canEdit ) ?></td>
<td class="colAudit"><?php echo makePopupLink( '?view=server&amp;id='.$row['Id'], 'zmServer', 'server', $row['zmaudit'] ? 'yes' : 'no', $canEdit ) ?></td>
<td class="colTrigger"><?php echo makePopupLink( '?view=server&amp;id='.$row['Id'], 'zmServer', 'server', $row['zmtrigger'] ? 'yes' : 'no', $canEdit ) ?></td>
@ -265,21 +265,22 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
<th class="colType"><?php echo translate('Type') ?></th>
<th class="colScheme"><?php echo translate('StorageScheme') ?></th>
<th class="colServer"><?php echo translate('Server') ?></th>
<th class="colDiskSpace"><?php echo translate('DiskSpace') ?></th>
<th class="colMark"><?php echo translate('Mark') ?></th>
</tr>
</thead>
<tbody>
<?php foreach( dbFetchAll( 'SELECT * FROM Storage ORDER BY Name' ) as $row ) { ?>
<?php foreach( Storage::find_all( null, array('order'=>'lower(Name)') ) as $Storage ) { ?>
<tr>
<td class="colId"><?php echo makePopupLink( '?view=storage&amp;id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($row['Id']), $canEdit ) ?></td>
<td class="colName"><?php echo makePopupLink( '?view=storage&amp;id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($row['Name']), $canEdit ) ?></td>
<td class="colPath"><?php echo makePopupLink( '?view=storage&amp;id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($row['Path']), $canEdit ) ?></td>
<td class="colType"><?php echo makePopupLink( '?view=storage&amp;id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($row['Type']), $canEdit ) ?></td>
<td class="colScheme"><?php echo makePopupLink( '?view=storage&amp;id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($row['Scheme']), $canEdit ) ?></td>
<td class="colId"><?php echo makePopupLink('?view=storage&amp;id='.$Storage->Id(), 'zmStorage', 'storage', validHtmlStr($Storage->Id()), $canEdit ) ?></td>
<td class="colName"><?php echo makePopupLink( '?view=storage&amp;id='.$Storage->Id(), 'zmStorage', 'storage', validHtmlStr($Storage->Name()), $canEdit ) ?></td>
<td class="colPath"><?php echo makePopupLink( '?view=storage&amp;id='.$Storage->Id(), 'zmStorage', 'storage', validHtmlStr($Storage->Path()), $canEdit ) ?></td>
<td class="colType"><?php echo makePopupLink( '?view=storage&amp;id='.$Storage->Id(), 'zmStorage', 'storage', validHtmlStr($Storage->Type()), $canEdit ) ?></td>
<td class="colScheme"><?php echo makePopupLink( '?view=storage&amp;id='.$Storage->Id(), 'zmStorage', 'storage', validHtmlStr($Storage->Scheme()), $canEdit ) ?></td>
<td class="colServer"><?php
$Server = new Server($row['ServerId']);
echo makePopupLink( '?view=storage&amp;id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($Server->Name()), $canEdit ) ?></td>
<td class="colMark"><input type="checkbox" name="markIds[]" value="<?php echo $row['Id'] ?>" onclick="configureDeleteButton(this);"<?php if ( !$canEdit ) { ?> disabled="disabled"<?php } ?>/></td>
echo makePopupLink( '?view=storage&amp;id='.$Storage->Id(), 'zmStorage', 'storage', validHtmlStr($Storage->Name()), $canEdit ) ?></td>
<td class="colDiskSpace"><?php echo human_filesize($Storage->disk_used_space()) . ' of ' . human_filesize($Storage->disk_total_space()) ?><?td>
<td class="colMark"><input type="checkbox" name="markIds[]" value="<?php echo $Storage->Id() ?>" onclick="configureDeleteButton(this);"<?php if ( !$canEdit ) { ?> disabled="disabled"<?php } ?>/></td>
</tr>
<?php } #end foreach Server ?>
</tbody>

View File

@ -102,6 +102,7 @@ while( $event = $result->fetch(PDO::FETCH_ASSOC) ) {
if ( count($EventsByMonitor[$event['MonitorId']]['Events']) ) {
$last_event = end($EventsByMonitor[$event['MonitorId']]['Events']);
#Logger::Debug(print_r($last_event,true));
$gap = $last_event->EndTimeSecs() - $event['StartTimeSecs'];
if ( $gap < $EventsByMonitor[$event['MonitorId']]['MinGap'] )
@ -140,7 +141,10 @@ while( $event = $result->fetch(PDO::FETCH_ASSOC) ) {
<tr>
<th class="colId"><?php echo translate('Id') ?></th>
<th class="colName"><i class="material-icons md-18">videocam</i>&nbsp;<?php echo translate('Name') ?></th>
<th class="colServer"><?php echo translate('Server') ?></th>
<th class="colEvents"><?php echo translate('Events') ?></th>
<th class="colFirstEvent"><?php echo translate('FirstEvent') ?></th>
<th class="colLastEvent"><?php echo translate('LastEvent') ?></th>
<th class="colMinGap"><?php echo translate('MinGap') ?></th>
<th class="colMaxGap"><?php echo translate('MaxGap') ?></th>
<th class="colMissingFiles"><?php echo translate('MissingFiles') ?></th>
@ -152,8 +156,25 @@ while( $event = $result->fetch(PDO::FETCH_ASSOC) ) {
for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
$monitor = $displayMonitors[$monitor_i];
$Monitor = new Monitor($monitor);
$montagereview_link = "?view=montagereview&live=0&MonitorId=". $monitor['Id'] . '&minTime='.$minTime.'&maxTime='.$maxTime;
if ( isset($EventsByMonitor[$Monitor->Id()]) ) {
$EventCounts = $EventsByMonitor[$Monitor->Id()];
$MinGap = $EventCounts['MinGap'];
$MaxGap = $EventCounts['MaxGap'];
$FileMissing = $EventCounts['FileMissing'];
$ZeroSize = $EventCounts['ZeroSize'];
$FirstEvent = $EventCounts['Events'][0];
$LastEvent = end($EventCounts['Events']);
} else {
$MinGap = 0;
$MaxGap = 0;
$FileMissing = 0;
$ZeroSize = 0;
$FirstEvent = 0;
$LastEvent = 0;
}
?>
<tr id="<?php echo 'monitor_id-'.$monitor['Id'] ?>" title="<?php echo $monitor['Id'] ?>">
<td class="colId"><a href="<?php echo $montagereview_link ?>"><?php echo $monitor['Id'] ?></a></td>
@ -168,11 +189,14 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
}, $Monitor->GroupIds() ) );
?>
</div></td>
<td class="colServer"><?php echo $Monitor->Server()->Name()?></td>
<td class="colEvents"><?php echo isset($EventsByMonitor[$Monitor->Id()])?count($EventsByMonitor[$Monitor->Id()]['Events']):0 ?></td>
<td class="colMinGap"><?php echo isset($EventsByMonitor[$Monitor->Id()])?$EventsByMonitor[$Monitor->Id()]['MinGap']:0 ?></td>
<td class="colMaxGap"><?php echo isset($EventsByMonitor[$Monitor->Id()])?$EventsByMonitor[$Monitor->Id()]['MaxGap']:0 ?></td>
<td class="colFileMissing"><?php echo isset($EventsByMonitor[$Monitor->Id()])?$EventsByMonitor[$Monitor->Id()]['FileMissing']:0 ?></td>
<td class="colZeroSize"><?php echo isset($EventsByMonitor[$Monitor->Id()])?$EventsByMonitor[$Monitor->Id()]['ZeroSize']:0 ?></td>
<td class="colFirstEvent"><?php echo $FirstEvent ? $FirstEvent->link_to($FirstEvent->Id().' at ' . $FirstEvent->StartTime()) : 'none'?></td>
<td class="colLastEvent"><?php echo $LastEvent ? $LastEvent->link_to($LastEvent->Id().' at ' . $LastEvent->StartTime()) : 'none'?></td>
<td class="colMinGap"><?php echo $MinGap ?></td>
<td class="colMaxGap"><?php echo $MaxGap ?></td>
<td class="colFileMissing<?php echo $FileMissing ? ' errorText' : ''?>"><?php echo $FileMissing ?></td>
<td class="colZeroSize<?php echo $ZeroSize ? ' errorText' : ''?>"><?php echo $ZeroSize ?></td>
</tr>
<?php
} # end for each monitor

View File

@ -41,7 +41,7 @@ if ( ! visibleMonitor( $mid ) ) {
$monitor = new Monitor( $mid );
#Whether to show the controls button
$showPtzControls = ( ZM_OPT_CONTROL && $monitor->Controllable() && canView( 'Control' ) );
$showPtzControls = ( ZM_OPT_CONTROL && $monitor->Controllable() && canView('Control') && $monitor->Type() != 'WebSite' );
if ( isset( $_REQUEST['scale'] ) ) {
$scale = validInt($_REQUEST['scale']);
@ -80,6 +80,7 @@ if ( canView( 'Control' ) && $monitor->Type() == 'Local' ) {
</div>
<div id="content">
<div id="imageFeed"><?php echo getStreamHTML( $monitor, array('scale'=>$scale) ); ?></div>
<?php if ( $monitor->Type() != 'WebSite' ) { ?>
<div id="monitorStatus">
<?php if ( canEdit( 'Monitors' ) ) { ?>
<div id="enableDisableAlarms"><a id="enableAlarmsLink" href="#" onclick="cmdEnableAlarms(); return( false );" class="hidden"><?php echo translate('EnableAlarms') ?></a><a id="disableAlarmsLink" href="#" onclick="cmdDisableAlarms(); return( false );" class="hidden"><?php echo translate('DisableAlarms') ?></a></div>
@ -119,7 +120,7 @@ if ( $streamMode == 'jpeg' ) {
?>
<input type="button" value="&ndash;" id="zoomOutBtn" title="<?php echo translate('ZoomOut') ?>" class="avail" onclick="streamCmdZoomOut()"/>
<?php
} // end if streamMode==jpeg
} // end if streamMode==jpeg
?>
</div>
<div id="replayStatus"<?php echo $streamMode=="single"?' class="hidden"':'' ?>>
@ -129,6 +130,7 @@ if ( $streamMode == 'jpeg' ) {
<span id="level"><?php echo translate('Buffer') ?>: <span id="levelValue"></span>%</span>
<span id="zoom"><?php echo translate('Zoom') ?>: <span id="zoomValue"></span>x</span>
</div>
<?php } // end if $monitor->Type() != 'WebSite' ?>
<?php
if ( $showPtzControls ) {
foreach ( getSkinIncludes( 'includes/control_functions.php' ) as $includeFile )
@ -139,7 +141,7 @@ if ( $showPtzControls ) {
</div>
<?php
}
if ( canView( 'Events' ) ) {
if ( canView( 'Events' ) && $monitor->Type() != 'WebSite' ) {
?>
<div id="events">
<table id="eventList" cellspacing="0">