Merge branch 'storageareas' into work_on_zms

This commit is contained in:
Isaac Connor 2017-09-01 17:53:59 -04:00
commit ed6ff23c2f
42 changed files with 1115 additions and 610 deletions

View File

@ -626,6 +626,7 @@ INSERT INTO `Controls` VALUES (NULL,'Vivotek ePTZ','Remote','Vivotek_ePTZ',0,0,1
INSERT INTO `Controls` VALUES (NULL,'Netcat ONVIF','Ffmpeg','Netcat',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,100,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,100,5,5,0,0,0,1,255,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Keekoon','Remote','Keekoon', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
INSERT INTO `Controls` VALUES (NULL,'HikVision','Local','',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,0,1,1,0,0,0,0,1,1,100,0,0,1,0,0,0,0,1,1,100,1,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Maginon Supra IPC','cURL','MaginonIPC',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,0,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
--
-- Add some monitor preset values

136
db/zm_update-1.31.3.sql Normal file
View File

@ -0,0 +1,136 @@
--
-- This adds StorageAreas
--
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.TABLES
WHERE table_name = 'Storage'
AND table_schema = DATABASE()
) > 0,
"SELECT 'Storage table exists'",
"CREATE TABLE `Storage` (
`Id` smallint(5) unsigned NOT NULL auto_increment,
`Path` varchar(64) NOT NULL default '',
`Name` varchar(64) NOT NULL default '',
PRIMARY KEY (`Id`)
)"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
--
-- Add StorageId column to Monitors
--
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Monitors'
AND table_schema = DATABASE()
AND column_name = 'StorageId'
) > 0,
"SELECT 'Column StorageId exists in Monitors'",
"ALTER TABLE Monitors ADD `StorageId` smallint(5) unsigned NOT NULL default 0 AFTER `ServerId`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
--
-- Add StorageId column to Eventss
--
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Events'
AND table_schema = DATABASE()
AND column_name = 'StorageId'
) > 0,
"SELECT 'Column StorageId exists in Events'",
"ALTER TABLE Events ADD `StorageId` smallint(5) unsigned NOT NULL default 0 AFTER `MonitorId`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
UPDATE Monitors SET StorageId = 0 WHERE StorageId IS NULL;
ALTER TABLE Monitors MODIFY `StorageId` smallint(5) unsigned NOT NULL default 0;
UPDATE Events SET StorageId = 0 WHERE StorageId IS NULL;
ALTER TABLE Events MODIFY `StorageId` smallint(5) unsigned NOT NULL default 0;
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Events'
AND table_schema = DATABASE()
AND column_name = 'Orientation'
) > 0,
"SELECT 'Column Orientation exists in Events'",
"ALTER TABLE `Events` ADD `Orientation` enum('0','90','180','270','hori','vert') NOT NULL default '0' AFTER `Notes`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
--
-- Update Monitors table to have an Index on ServerId
--
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.STATISTICS
WHERE table_name = 'Monitors'
AND table_schema = DATABASE()
AND index_name = 'Monitors_ServerId_idx'
) > 0,
"SELECT 'Monitors_ServerId Index already exists on Monitors table'",
"CREATE INDEX `Monitors_ServerId_idx` ON `Monitors` (`ServerId`)"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
--
-- Update Server table to have an Index on Name
--
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.STATISTICS
WHERE table_name = 'Servers'
AND table_schema = DATABASE()
AND index_name = 'Servers_Name_idx'
) > 0,
"SELECT 'Servers_Name Index already exists on Servers table'",
"CREATE INDEX `Servers_Name_idx` ON `Servers` (`Name`)"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
-- ALTER TABLE Logs ALTER Message DROP DEFAULT;
ALTER TABLE Logs MODIFY Message TEXT NOT NULL;
ALTER TABLE Config MODIFY DefaultValue TEXT;
--
-- Add an Id column and make it the primary key of the Filters table
--
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Filters'
AND table_schema = DATABASE()
AND column_name = 'Id'
) > 0,
"SELECT 'Column Id exists in Filters'",
"ALTER TABLE `Filters` DROP PRIMARY KEY, ADD `Id` int(10) unsigned NOT NULL auto_increment PRIMARY KEY FIRST, ADD KEY `Name` (`Name`);"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

View File

@ -16,7 +16,7 @@ Alias /zm "@ZM_WEBDIR@"
DirectoryIndex index.php
SSLRequireSSL
Options -Indexes +MultiViews +FollowSymLinks
AllowOverride All
AllowOverride None
<IfModule mod_authz_core.c>
# Apache 2.4
Require all granted
@ -31,7 +31,7 @@ Alias /zm "@ZM_WEBDIR@"
ScriptAlias /cgi-bin-zm "@ZM_CGIDIR@"
<Directory "@ZM_CGIDIR@">
SSLRequireSSL
AllowOverride All
AllowOverride None
Options +ExecCGI +FollowSymLinks
<IfModule mod_authz_core.c>
# Apache 2.4
@ -44,3 +44,28 @@ ScriptAlias /cgi-bin-zm "@ZM_CGIDIR@"
</IfModule>
</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 "@ZM_WEBDIR@/api">
RewriteEngine on
RewriteRule ^$ app/webroot/ [L]
RewriteRule (.*) app/webroot/$1 [L]
RewriteBase /zm/api
</Directory>
<Directory "@ZM_WEBDIR@/api/app">
RewriteEngine on
RewriteRule ^$ webroot/ [L]
RewriteRule (.*) webroot/$1 [L]
RewriteBase /zm/api
</Directory>
<Directory "@ZM_WEBDIR@/api/app/webroot">
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
RewriteBase /zm/api
</Directory>

View File

@ -11,18 +11,61 @@ RewriteRule ^/?(zm)(.*) https://%{SERVER_NAME}/$1$2 [R,L]
Alias /zm "@ZM_WEBDIR@"
<Directory "@ZM_WEBDIR@">
# explicitly set index.php as the only directoryindex
DirectoryIndex disabled
DirectoryIndex index.php
SSLRequireSSL
Options -Indexes MultiViews FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
Options -Indexes +MultiViews +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 /cgi-bin-zm "@ZM_CGIDIR@"
<Directory "@ZM_CGIDIR@">
SSLRequireSSL
AllowOverride All
Options ExecCGI FollowSymLinks
Order allow,deny
Allow from all
AllowOverride None
Options +ExecCGI +FollowSymLinks
<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>
# 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 "@ZM_WEBDIR@/api">
RewriteEngine on
RewriteRule ^$ app/webroot/ [L]
RewriteRule (.*) app/webroot/$1 [L]
RewriteBase /zm/api
</Directory>
<Directory "@ZM_WEBDIR@/api/app">
RewriteEngine on
RewriteRule ^$ webroot/ [L]
RewriteRule (.*) webroot/$1 [L]
RewriteBase /zm/api
</Directory>
<Directory "@ZM_WEBDIR@/api/app/webroot">
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
RewriteBase /zm/api
</Directory>

View File

@ -109,6 +109,7 @@ Requires: perl(MIME::Lite)
Requires: perl(Net::SMTP)
Requires: perl(Net::FTP)
Requires: perl(LWP::Protocol::https)
Requires: ca-certificates
%{?with_init_systemd:Requires(post): systemd}
%{?with_init_systemd:Requires(post): systemd-sysv}
@ -162,7 +163,14 @@ too much degradation of performance.
%make_install
# Remove unwanted files and folders
find %{buildroot} \( -name .packlist -or -name .git -or -name .gitignore -or -name .gitattributes -or -name .travis.yml \) -type f -delete > /dev/null 2>&1 || :
find %{buildroot} \( -name .htaccess -or -name .editorconfig -or -name .packlist -or -name .git -or -name .gitignore -or -name .gitattributes -or -name .travis.yml \) -type f -delete > /dev/null 2>&1 || :
# Recursively change shebang in all relevant scripts and set execute permission
find %{buildroot}%{_datadir}/zoneminder/www/api \( -name cake -or -name cake.php \) -type f -exec sed -i 's\^#!/usr/bin/env bash$\#!/usr/bin/bash\' {} \; -exec %{__chmod} 755 {} \;
# Use the system cacert file rather then the one bundled with CakePHP
%{__rm} -f %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem
%{__ln_s} ../../../../../../../..%{_sysconfdir}/pki/tls/certs/ca-bundle.crt %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem
%post
%if 0%{?with_init_sysv}
@ -297,6 +305,7 @@ rm -rf %{_docdir}/%{name}-%{version}
%{_unitdir}/zoneminder.service
%{_datadir}/polkit-1/actions/com.zoneminder.systemctl.policy
%{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules
%{_bindir}/zmsystemctl.pl
%endif
%if 0%{?with_init_sysv}
@ -318,7 +327,6 @@ rm -rf %{_docdir}/%{name}-%{version}
%{_bindir}/zmvideo.pl
%{_bindir}/zmwatch.pl
%{_bindir}/zmcamtool.pl
%{_bindir}/zmsystemctl.pl
%{_bindir}/zmtelemetry.pl
%{_bindir}/zmx10.pl
%{_bindir}/zmonvif-probe.pl

View File

@ -101,7 +101,7 @@ How filters actually work
--------------------------
It is useful to know how filters actually work behind the scenes in ZoneMinder, in the event you find your filter not functioning as intended:
* the primary filter processing process in ZoneMinder is a perl file called ``zmfilter.pl``
* the primary filter processing process in ZoneMinder is a perl file called ``zmfilter.pl`` which retrieves filters from the Filters database table
* zmfilter.pl runs every FILTER_EXECUTE_INTERVAL seconds (default is 20s, can be changed in Options->System)
* in each run, it goes through all the filters which are marked as "Run in Background" and if the conditions match performs the specified action
* zmfilter.pl also reloads all the filters every FILTER_RELOAD_DELAY seconds (default is 300s/5mins, can be changed in Options->System)

View File

@ -250,16 +250,9 @@ sub profiles
my $profiles = $result->get_Profiles();
foreach my $profile ( @{ $profiles } ) {
foreach my $profile ( @{ $profiles } ) {
my $token = $profile->attr()->get_token() ;
print $token . ", " .
$profile->get_Name() . ", " .
$profile->get_VideoEncoderConfiguration()->get_Encoding() . ", " .
$profile->get_VideoEncoderConfiguration()->get_Resolution()->get_Width() . ", " .
$profile->get_VideoEncoderConfiguration()->get_Resolution()->get_Height() . ", " .
$profile->get_VideoEncoderConfiguration()->get_RateControl()->get_FrameRateLimit() .
", ";
# Specification gives conflicting values for unicast stream types, try both.
# http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl#op.GetStreamUri
@ -278,9 +271,18 @@ sub profiles
die $result if not $result;
# print $result . "\n";
print $result->get_MediaUri()->get_Uri() .
"\n";
}
my $VideoEncoderConfiguration = $profile->get_VideoEncoderConfiguration();
print join(', ', $token,
$profile->get_Name(),
( $VideoEncoderConfiguration ? (
$VideoEncoderConfiguration->get_Encoding(),
$VideoEncoderConfiguration->get_Resolution()->get_Width(),
$VideoEncoderConfiguration->get_Resolution()->get_Height(),
$VideoEncoderConfiguration->get_RateControl()->get_FrameRateLimit(),
) : () ),
$result->get_MediaUri()->get_Uri() ,
). "\n";
} # end foreach profile
#
# use message parser without schema validation ???

View File

@ -0,0 +1,333 @@
# ==========================================================================
#
# ZoneMinder Maginon Supra IPC Camera Control Protocol Module,
# Copyright (C) 2017 Martin Gutenbrunner (martin.gutenbrunner@SPAMsonic.net)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ==========================================================================
#
# This module contains the implementation of the Maginon Supra IPC camera
# procotol version.
#
package ZoneMinder::Control::MaginonIPC;
use 5.006;
use strict;
use warnings;
require ZoneMinder::Base;
require ZoneMinder::Control;
our @ISA = qw(ZoneMinder::Control);
# ==========================================================================
#
# Maginon Supra IPC IP Camera Control Protocol
#
# ==========================================================================
use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open
{
my $self = shift;
$self->loadMonitor();
use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
$self->{state} = 'open';
}
sub close
{
my $self = shift;
$self->{state} = 'closed';
}
sub printMsg
{
my $self = shift;
my $msg = shift;
my $msg_len = length($msg);
Debug( $msg."[".$msg_len."]" );
}
sub sendCmd
{
my $self = shift;
my $cmd = shift;
#my $result = undef;
printMsg( $cmd, "Tx" );
my $url = "http://".$self->{Monitor}->{ControlAddress}."$cmd";
# Info($url);
my $req = HTTP::Request->new( GET=>$url );
my $res = $self->{ua}->request($req);
return( !undef );
}
sub moveStop
{
Debug("moveStop");
my $self = shift;
my $params = shift;
my $cmd = "/decoder_control.cgi?command=1";
$self->sendCmd( $cmd );
}
sub moveConUp
{
Debug("moveConUp");
my $self = shift;
my $params = shift;
my $cmd = "/decoder_control.cgi?command=0";
$self->sendCmd( $cmd );
my $autostop = $self->getParam( $params, 'autostop', 0 );
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
{
usleep( $self->{Monitor}->{AutoStopTimeout} );
$self->moveStop( $params );
}
}
sub moveConDown
{
Debug("moveConDown");
my $self = shift;
my $params = shift;
my $cmd = "/decoder_control.cgi?command=2";
$self->sendCmd( $cmd );
my $autostop = $self->getParam( $params, 'autostop', 0 );
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
{
usleep( $self->{Monitor}->{AutoStopTimeout} );
$self->moveStop( $params );
}
}
sub moveConLeft
{
Debug("moveConLeft");
my $self = shift;
my $params = shift;
my $cmd = "/decoder_control.cgi?command=4";
$self->sendCmd( $cmd );
my $autostop = $self->getParam( $params, 'autostop', 0 );
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
{
usleep( $self->{Monitor}->{AutoStopTimeout} );
$self->moveStop( $params );
}
}
sub moveConRight
{
Debug("moveConRight");
my $self = shift;
my $params = shift;
my $cmd = "/decoder_control.cgi?command=6";
$self->sendCmd( $cmd );
my $autostop = $self->getParam( $params, 'autostop', 0 );
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
{
usleep( $self->{Monitor}->{AutoStopTimeout} );
$self->moveStop( $params );
}
}
sub moveConUpRight
{
Debug("moveConUpRight");
my $self = shift;
my $params = shift;
my $cmd = "/decoder_control.cgi?command=91";
$self->sendCmd( $cmd );
my $autostop = $self->getParam( $params, 'autostop', 0 );
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
{
usleep( $self->{Monitor}->{AutoStopTimeout} );
$self->moveStop( $params );
}
}
sub moveConUpLeft
{
Debug("moveConUpLeft");
my $self = shift;
my $params = shift;
my $cmd = "/decoder_control.cgi?command=90";
$self->sendCmd( $cmd );
my $autostop = $self->getParam( $params, 'autostop', 0 );
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
{
usleep( $self->{Monitor}->{AutoStopTimeout} );
$self->moveStop( $params );
}
}
sub moveConDownRight
{
Debug("moveConDownRight");
my $self = shift;
my $params = shift;
my $cmd = "/decoder_control.cgi?command=93";
$self->sendCmd( $cmd );
my $autostop = $self->getParam( $params, 'autostop', 0 );
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
{
usleep( $self->{Monitor}->{AutoStopTimeout} );
$self->moveStop( $params );
}
}
sub moveConDownLeft
{
Debug("moveConDownLeft");
my $self = shift;
my $params = shift;
my $cmd = "/decoder_control.cgi?command=92";
$self->sendCmd( $cmd );
my $autostop = $self->getParam( $params, 'autostop', 0 );
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
{
usleep( $self->{Monitor}->{AutoStopTimeout} );
$self->moveStop( $params );
}
}
sub presetSet
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
Info( "Set Preset $preset" );
my $cmdNum;
if ($preset == 1) {
$cmdNum = 30;
} elsif ($preset == 2) {
$cmdNum = 32;
} elsif ($preset == 3) {
$cmdNum = 34;
} elsif ($preset == 4) {
$cmdNum = 36;
} else {
$cmdNum = 36;
}
my $cmd = "/decoder_control.cgi?command=$cmdNum";
$self->sendCmd( $cmd );
}
sub presetGoto
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
Info( "Goto Preset $preset" );
my $cmdNum;
if ($preset == 1) {
$cmdNum = 31;
} elsif ($preset == 2) {
$cmdNum = 33;
} elsif ($preset == 3) {
$cmdNum = 35;
} elsif ($preset == 4) {
$cmdNum = 37;
} else {
$cmdNum = 37;
}
my $cmd = "/decoder_control.cgi?command=$cmdNum";
$self->sendCmd( $cmd );
}
1;
__END__
# Below is stub documentation for your module. You'd better edit it!
=head1 NAME
ZoneMinder::Control::MaginonIPC - Zoneminder PTZ control module for the Maginon Supra-IPC 40 IP Camera
=head1 SYNOPSIS
use ZoneMinder::Control::MaginonIPC;
blah blah blah
=head1 DESCRIPTION
This is for Zoneminder PTZ control module for the Maginon Supra-IPC 40 camera. It probably also works with other models.
=head2 EXPORT
None by default.
=head1 SEE ALSO
www.zoneminder.com
=head1 AUTHOR
Martin Gutenbrunner, E<lt>martin.gutenbrunner@gmx.atE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2017 by Martin Gutenbrunner
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.3 or,
at your option, any later version of Perl 5 you may have available.
=cut

View File

@ -2,7 +2,7 @@
#
# ==========================================================================
#
# ZoneMinder External Trigger Script, $Date: 2008-07-25 10:48:16 +0100 (Fri, 25 Jul 2008) $, $Revision: 2612 $
# ZoneMinder External Trigger Script
# Copyright (C) 2001-2008 Philip Coombes
#
# This program is free software; you can redistribute it and/or
@ -21,92 +21,6 @@
#
# ==========================================================================
=head1 NAME
zmtrigger.pl - ZoneMinder External Trigger Script
=head1 DESCRIPTION
This script is used to trigger and cancel alarms from external connections
using an arbitrary text based format.
This script offers generic solution to external triggering of alarms. It
can handle external connections via either internet socket, unix socket or
file/device interfaces. You can either use it 'as is' if you can interface
with the existing format, or override connections and channels to customise
it to your needs.
If enabled by the OPT_TRIGGERS option, Zoneminder service start
zmtrigger.pl which listens for control messages on TCP port 6802.
=head1 TRIGGER MESSAGE FORMAT
B<id>|B<action>|B<score>|B<cause>|B<text>|B<showtext>
=over 4
=item B<id>
is the id number or name of the ZM monitor.
=item B<action>
Valid actions are 'on', 'off', 'cancel' or 'show' where
'on' forces an alarm condition on;
'off' forces an alarm condition off;
'cancel' negates the previous 'on' or 'off';
'show' updates the auxiliary text represented by the %Q
placeholder, which can optionally be added to the affected monitor's
timestamp label format.
Ordinarily you would use 'on' and 'cancel', 'off' would tend to be
used to suppress motion based events. Additionally 'on' and 'off' can
take an additional time offset, e.g. on+20 which automatically
cancel's the previous action after that number of seconds.
=item B<score>
is the score given to the alarm, usually to indicate it's
importance. For 'on' triggers it should be non-zero, otherwise it should
be zero.
=item B<cause>
is a 32 char max string indicating the reason for, or source of
the alarm e.g. 'Relay 1 open'. This is saved in the 'Cause' field of the
event. Ignored for 'off' or 'cancel' messages.
=item B<text>
is a 256 char max additional info field, which is saved in the
'Description' field of an event. Ignored for 'off' or 'cancel' messages.
=item B<showtext>
is up to 32 characters of text that can be displayed in the
timestamp that is added to images. The 'show' action is designed to
update this text without affecting alarms but the text is updated, if
present, for any of the actions. This is designed to allow external input
to appear on the images captured, for instance temperature or personnel
identity etc.
=back
Note that multiple messages can be sent at once and should be LF or CRLF
delimited. This script is not necessarily intended to be a solution in
itself, but is intended to be used as 'glue' to help ZoneMinder interface
with other systems. It will almost certainly require some customisation
before you can make any use of it. If all you want to do is generate alarms
from external sources then using the ZoneMinder::SharedMem perl module is
likely to be easier.
=head1 EXAMPLES
3|on+10|1|motion|text|showtext
Triggers 'alarm' on camera #3 for 10 seconds with score=1, cause='motion'.
=cut
use strict;
use bytes;
@ -198,7 +112,6 @@ my $monitor_reload_time = 0;
my $needsReload = 0;
loadMonitors();
$! = undef;
my $rin = '';
my $win = $rin;
@ -216,20 +129,19 @@ while( 1 ) {
if ( $nfound > 0 ) {
Debug( "Got input from $nfound connections\n" );
foreach my $connection ( @in_select_connections ) {
if ( vec( $rout, $connection->fileno(), 1 ) ) {
Debug( 'Got input from connection '
.$connection->name()
." ("
.' ('
.$connection->fileno()
.")\n"
);
if ( $connection->spawns() ) {
my $new_connection = $connection->accept();
$spawned_connections{$new_connection->fileno()} = $new_connection;
Debug( "Added new spawned connection ("
Debug( 'Added new spawned connection ('
.$new_connection->fileno()
."), "
.'), '
.int(keys(%spawned_connections))
." spawned connections\n"
);
@ -241,14 +153,14 @@ while( 1 ) {
}
}
}
}
}
} # end foreach connection
foreach my $connection ( values(%spawned_connections) ) {
if ( vec( $rout, $connection->fileno(), 1 ) ) {
Debug( 'Got input from spawned connection '
.$connection->name()
." ("
.' ('
.$connection->fileno()
.")\n"
);
@ -259,16 +171,16 @@ while( 1 ) {
}
} else {
delete( $spawned_connections{$connection->fileno()} );
Debug( "Removed spawned connection ("
Debug( 'Removed spawned connection ('
.$connection->fileno()
."), "
.'), '
.int(keys(%spawned_connections))
." spawned connections\n"
);
$connection->close();
}
}
} # end foreach connection
} # end foreach spawned connection
} elsif ( $nfound < 0 ) {
if ( $! == EINTR ) {
# Do nothing
@ -299,33 +211,44 @@ while( 1 ) {
}
my ( $state, $last_event ) = zmMemRead( $monitor,
[ 'shared_data:state', 'shared_data:last_event' ]
);
[
'shared_data:state',
'shared_data:last_event'
]
);
#print( "$monitor->{Id}: S:$state, LE:$last_event\n" );
#print( "$monitor->{Id}: mS:$monitor->{LastState}, mLE:$monitor->{LastEvent}\n" );
if ( $state == STATE_ALARM || $state == STATE_ALERT ) { # In alarm state
if ( $state == STATE_ALARM || $state == STATE_ALERT ) {
# In alarm state
if ( !defined($monitor->{LastEvent})
|| ($last_event != $monitor->{LastEvent})
) { # A new event
push( @out_messages, $monitor->{Id}.'|on|'.time().'|'.$last_event );
} else { # The same one as last time, so ignore it
) {
# A new event
push( @out_messages, $monitor->{Id}."|on|".time()."|".$last_event );
} else {
# The same one as last time, so ignore it
# Do nothing
}
} elsif ( ($state == STATE_IDLE && $monitor->{LastState} != STATE_IDLE)
|| ($state == STATE_TAPE && $monitor->{LastState} != STATE_TAPE
)) { # Out of alarm state
} elsif (
($state == STATE_IDLE && $monitor->{LastState} != STATE_IDLE)
||
($state == STATE_TAPE && $monitor->{LastState} != STATE_TAPE)
) {
# Out of alarm state
push( @out_messages, $monitor->{Id}.'|off|'.time().'|'.$last_event );
} elsif ( defined($monitor->{LastEvent})
&& ($last_event != $monitor->{LastEvent})
) { # We've missed a whole event
} elsif (
defined($monitor->{LastEvent})
&&
($last_event != $monitor->{LastEvent})
) {
# We've missed a whole event
push( @out_messages, $monitor->{Id}.'|on|'.time().'|'.$last_event );
push( @out_messages, $monitor->{Id}.'|off|'.time().'|'.$last_event );
}
$monitor->{LastState} = $state;
$monitor->{LastEvent} = $last_event;
} # end foreach monitor
foreach my $connection ( @out_connections ) {
if ( $connection->canWrite() ) {
$connection->putMessages( \@out_messages );
@ -379,7 +302,9 @@ while( 1 ) {
loadMonitors();
$needsReload = 0;
}
}
# zmDbConnect will ping and reconnect if neccessary
$dbh = zmDbConnect();
} # end while ( 1 )
Info( "Trigger daemon exiting\n" );
exit;
@ -389,9 +314,9 @@ sub loadMonitors {
my %new_monitors = ();
my $sql = "SELECT * FROM Monitors WHERE find_in_set( Function, 'Modect,Mocord,Nodect' )".
( $Config{ZM_SERVER_ID} ? 'AND ServerId=?' : '' ).
'ORDER BY Id DESC'
my $sql = "SELECT * FROM Monitors
WHERE find_in_set( Function, 'Modect,Mocord,Nodect' )".
( $Config{ZM_SERVER_ID} ? 'AND ServerId=?' : '' )
;
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
@ -450,7 +375,7 @@ sub handleMessage {
handleDelay($delay, $connection, $action_text);
}
} elsif ( $action =~ /^(on|off)(?:[ \+](\d+))?$/ ) {
next if ( !$monitor->{Enabled} );
next if !$monitor->{Enabled};
my $trigger = $1;
my $delay = $2;
@ -473,7 +398,9 @@ sub handleMessage {
zmTriggerShowtext( $monitor, $showtext ) if defined($showtext);
Info( "Trigger '$trigger'\n" );
# Wait til it's finished
while( zmInAlarm( $monitor ) && ($last_event == zmGetLastEvent( $monitor ))) {
while( zmInAlarm( $monitor )
&& ($last_event == zmGetLastEvent( $monitor ))
) {
# Tenth of a second
usleep( 100000 );
}
@ -507,3 +434,90 @@ sub handleDelay {
}
1;
__END__
=head1 NAME
zmtrigger.pl - ZoneMinder External Trigger Script
=head1 DESCRIPTION
This script is used to trigger and cancel alarms from external connections
using an arbitrary text based format.
This script offers generic solution to external triggering of alarms. It
can handle external connections via either internet socket, unix socket or
file/device interfaces. You can either use it 'as is' if you can interface
with the existing format, or override connections and channels to customise
it to your needs.
If enabled by the OPT_TRIGGERS option, Zoneminder service start
zmtrigger.pl which listens for control messages on TCP port 6802.
=head1 TRIGGER MESSAGE FORMAT
B<id>|B<action>|B<score>|B<cause>|B<text>|B<showtext>
=over 4
=item B<id>
is the id number or name of the ZM monitor.
=item B<action>
Valid actions are 'on', 'off', 'cancel' or 'show' where
'on' forces an alarm condition on;
'off' forces an alarm condition off;
'cancel' negates the previous 'on' or 'off';
'show' updates the auxiliary text represented by the %Q
placeholder, which can optionally be added to the affected monitor's
timestamp label format.
Ordinarily you would use 'on' and 'cancel', 'off' would tend to be
used to suppress motion based events. Additionally 'on' and 'off' can
take an additional time offset, e.g. on+20 which automatically
cancel's the previous action after that number of seconds.
=item B<score>
is the score given to the alarm, usually to indicate it's
importance. For 'on' triggers it should be non-zero, otherwise it should
be zero.
=item B<cause>
is a 32 char max string indicating the reason for, or source of
the alarm e.g. 'Relay 1 open'. This is saved in the 'Cause' field of the
event. Ignored for 'off' or 'cancel' messages.
=item B<text>
is a 256 char max additional info field, which is saved in the
'Description' field of an event. Ignored for 'off' or 'cancel' messages.
=item B<showtext>
is up to 32 characters of text that can be displayed in the
timestamp that is added to images. The 'show' action is designed to
update this text without affecting alarms but the text is updated, if
present, for any of the actions. This is designed to allow external input
to appear on the images captured, for instance temperature or personnel
identity etc.
=back
Note that multiple messages can be sent at once and should be LF or CRLF
delimited. This script is not necessarily intended to be a solution in
itself, but is intended to be used as 'glue' to help ZoneMinder interface
with other systems. It will almost certainly require some customisation
before you can make any use of it. If all you want to do is generate alarms
from external sources then using the ZoneMinder::SharedMem perl module is
likely to be easier.
=head1 EXAMPLES
3|on+10|1|motion|text|showtext
Triggers 'alarm' on camera #3 for 10 seconds with score=1, cause='motion'.
=cut

View File

@ -237,7 +237,7 @@ if ( $concat_name ) {
}
close $fd;
my $command = $Config{ZM_PATH_FFMPEG}
. " -f concat -i $concat_list_file -c copy "
. " -f concat -safe 0 -i $concat_list_file -c copy "
.$Config{ZM_FFMPEG_OUTPUT_OPTIONS}
." '$video_file' > $Config{ZM_PATH_LOGS}/ffmpeg_${concat_name}.log 2>&1"
;

View File

@ -27,10 +27,11 @@ MYSQL dbconn;
int zmDbConnected = false;
void zmDbConnect()
{
if ( !mysql_init( &dbconn ) )
{
void zmDbConnect() {
if ( zmDbConnected )
return;
if ( !mysql_init( &dbconn ) ) {
Error( "Can't initialise database connection: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
@ -38,48 +39,35 @@ void zmDbConnect()
if ( mysql_options( &dbconn, MYSQL_OPT_RECONNECT, &reconnect ) )
Fatal( "Can't set database auto reconnect option: %s", mysql_error( &dbconn ) );
std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":" );
if ( colonIndex == std::string::npos )
{
if ( !mysql_real_connect( &dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, NULL, 0 ) )
{
if ( colonIndex == std::string::npos ) {
if ( !mysql_real_connect( &dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, NULL, 0 ) ) {
Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
}
else
{
} else {
std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex );
std::string dbPortOrSocket = staticConfig.DB_HOST.substr( colonIndex+1 );
if ( dbPortOrSocket[0] == '/' )
{
if ( !mysql_real_connect( &dbconn, NULL, staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, dbPortOrSocket.c_str(), 0 ) )
{
if ( dbPortOrSocket[0] == '/' ) {
if ( !mysql_real_connect( &dbconn, NULL, staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, dbPortOrSocket.c_str(), 0 ) ) {
Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
}
else
{
if ( !mysql_real_connect( &dbconn, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, atoi(dbPortOrSocket.c_str()), NULL, 0 ) )
{
} else {
if ( !mysql_real_connect( &dbconn, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, atoi(dbPortOrSocket.c_str()), NULL, 0 ) ) {
Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
}
}
if ( mysql_select_db( &dbconn, staticConfig.DB_NAME.c_str() ) )
{
if ( mysql_select_db( &dbconn, staticConfig.DB_NAME.c_str() ) ) {
Error( "Can't select database: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
zmDbConnected = true;
}
void zmDbClose()
{
if ( zmDbConnected )
{
void zmDbClose() {
if ( zmDbConnected ) {
mysql_close( &dbconn );
// mysql_init() call implicitly mysql_library_init() but
// mysql_close() does not call mysql_library_end()

View File

@ -155,6 +155,7 @@ bool EventStream::loadEventData( int event_id ) {
else
snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%ld", staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_data->event_id );
}
delete storage; storage = NULL;
updateFrameRate( (double)event_data->frame_count/event_data->duration );

View File

@ -56,7 +56,7 @@ FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::stri
mRawFrame = NULL;
mFrame = NULL;
frameCount = 0;
startTime=0;
startTime = 0;
mIsOpening = false;
mCanCapture = false;
mOpenStart = 0;

View File

@ -21,6 +21,7 @@
#include "zm_config.h"
#include "zm_utils.h"
#include "zm_db.h"
#include <unistd.h>
#include <stdio.h>
@ -333,45 +334,7 @@ Logger::Level Logger::databaseLevel( Logger::Level databaseLevel ) {
databaseLevel = limit(databaseLevel);
if ( mDatabaseLevel != databaseLevel ) {
if ( databaseLevel > NOLOG && mDatabaseLevel <= NOLOG ) {
if ( !mDbConnected ) {
if ( !mysql_init( &mDbConnection ) ) {
Fatal( "Can't initialise database connection: %s", mysql_error( &mDbConnection ) );
exit( mysql_errno( &mDbConnection ) );
}
my_bool reconnect = 1;
if ( mysql_options( &mDbConnection, MYSQL_OPT_RECONNECT, &reconnect ) )
Fatal( "Can't set database auto reconnect option: %s", mysql_error( &mDbConnection ) );
std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":" );
if ( colonIndex == std::string::npos ) {
if ( !mysql_real_connect( &mDbConnection, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, NULL, 0 ) ) {
Fatal( "Can't connect to database: %s", mysql_error( &mDbConnection ) );
exit( mysql_errno( &mDbConnection ) );
}
} else {
std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex );
std::string dbPortOrSocket = staticConfig.DB_HOST.substr( colonIndex+1 );
if ( dbPortOrSocket[0] == '/' ) {
if ( !mysql_real_connect( &mDbConnection, NULL, staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, dbPortOrSocket.c_str(), 0 ) ) {
Fatal( "Can't connect to database: %s", mysql_error( &mDbConnection ) );
exit( mysql_errno( &mDbConnection ) );
}
} else {
if ( !mysql_real_connect( &mDbConnection, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, atoi(dbPortOrSocket.c_str()), NULL, 0 ) ) {
Fatal( "Can't connect to database: %s", mysql_error( &mDbConnection ) );
exit( mysql_errno( &mDbConnection ) );
}
}
} // end if has colon
unsigned long mysqlVersion = mysql_get_server_version( &mDbConnection );
if ( mysqlVersion < 50019 )
if ( mysql_options( &mDbConnection, MYSQL_OPT_RECONNECT, &reconnect ) )
Fatal( "Can't set database auto reconnect option: %s", mysql_error( &mDbConnection ) );
if ( mysql_select_db( &mDbConnection, staticConfig.DB_NAME.c_str() ) ) {
Fatal( "Can't select database: %s", mysql_error( &mDbConnection ) );
exit( mysql_errno( &mDbConnection ) );
}
mDbConnected = true;
} // end if ! mDbConnected
zmDbConnect();
} // end if ( databaseLevel > NOLOG && mDatabaseLevel <= NOLOG )
mDatabaseLevel = databaseLevel;
} // end if ( mDatabaseLevel != databaseLevel )
@ -439,10 +402,7 @@ void Logger::closeFile() {
}
void Logger::closeDatabase() {
if ( mDbConnected ) {
mysql_close( &mDbConnection );
mDbConnected = false;
}
}
void Logger::openSyslog() {
@ -548,13 +508,13 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
char sql[ZM_SQL_MED_BUFSIZ];
char escapedString[(strlen(syslogStart)*2)+1];
mysql_real_escape_string( &mDbConnection, escapedString, syslogStart, strlen(syslogStart) );
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( &mDbConnection, sql ) ) {
if ( mysql_query( &dbconn, sql ) ) {
Level tempDatabaseLevel = mDatabaseLevel;
databaseLevel( NOLOG );
Error( "Can't insert log entry: sql(%s) error(%s)", sql, mysql_error( &mDbConnection ) );
Error( "Can't insert log entry: sql(%s) error(%s)", sql, mysql_error( &dbconn ) );
databaseLevel(tempDatabaseLevel);
}
}
@ -566,6 +526,8 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
free(filecopy);
if ( level <= FATAL ) {
logTerm();
zmDbClose();
if ( level <= PANIC )
abort();
exit( -1 );

View File

@ -27,16 +27,14 @@
#include <string.h>
#include <time.h>
User::User()
{
User::User() {
username[0] = password[0] = 0;
enabled = false;
stream = events = control = monitors = system = PERM_NONE;
monitor_ids = 0;
}
User::User( MYSQL_ROW &dbrow )
{
User::User( MYSQL_ROW &dbrow ) {
int index = 0;
strncpy( username, dbrow[index++], sizeof(username) );
strncpy( password, dbrow[index++], sizeof(password) );
@ -48,22 +46,18 @@ User::User( MYSQL_ROW &dbrow )
system = (Permission)atoi( dbrow[index++] );
monitor_ids = 0;
char *monitor_ids_str = dbrow[index++];
if ( monitor_ids_str && *monitor_ids_str )
{
if ( monitor_ids_str && *monitor_ids_str ) {
monitor_ids = new int[strlen(monitor_ids_str)];
int n_monitor_ids = 0;
const char *ptr = monitor_ids_str;
do
{
do {
int id = 0;
while( isdigit( *ptr ) )
{
while( isdigit( *ptr ) ) {
id *= 10;
id += *ptr-'0';
ptr++;
}
if ( id )
{
if ( id ) {
monitor_ids[n_monitor_ids++] = id;
if ( !*ptr )
break;
@ -75,21 +69,16 @@ User::User( MYSQL_ROW &dbrow )
}
}
User::~User()
{
User::~User() {
delete monitor_ids;
}
bool User::canAccess( int monitor_id )
{
if ( !monitor_ids )
{
bool User::canAccess( int monitor_id ) {
if ( !monitor_ids ) {
return( true );
}
for ( int i = 0; monitor_ids[i]; i++ )
{
if ( monitor_ids[i] == monitor_id )
{
for ( int i = 0; monitor_ids[i]; i++ ) {
if ( monitor_ids[i] == monitor_id ) {
return( true );
}
}
@ -98,8 +87,7 @@ bool User::canAccess( int monitor_id )
// Function to load a user from username and password
// Please note that in auth relay mode = none, password is NULL
User *zmLoadUser( const char *username, const char *password )
{
User *zmLoadUser( const char *username, const char *password ) {
char sql[ZM_SQL_SML_BUFSIZ] = "";
char safer_username[65]; // current db username size is 32
char safer_password[129]; // current db password size is 64
@ -114,22 +102,20 @@ User *zmLoadUser( const char *username, const char *password )
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Enabled = 1", safer_username );
}
if ( mysql_query( &dbconn, sql ) )
{
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
MYSQL_RES *result = mysql_store_result( &dbconn );
if ( !result )
{
if ( !result ) {
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
int n_users = mysql_num_rows( result );
if ( n_users != 1 )
{
if ( n_users != 1 ) {
mysql_free_result( result );
Warning( "Unable to authenticate user %s", username );
return( 0 );
}
@ -145,13 +131,11 @@ User *zmLoadUser( const char *username, const char *password )
}
// Function to validate an authentication string
User *zmLoadAuthUser( const char *auth, bool use_remote_addr )
{
User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) {
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
#ifdef HAVE_GCRYPT_H
// Special initialisation for libgcrypt
if ( !gcry_check_version( GCRYPT_VERSION ) )
{
if ( !gcry_check_version( GCRYPT_VERSION ) ) {
Fatal( "Unable to initialise libgcrypt" );
}
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
@ -159,11 +143,9 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr )
#endif // HAVE_GCRYPT_H
const char *remote_addr = "";
if ( use_remote_addr )
{
if ( use_remote_addr ) {
remote_addr = getenv( "REMOTE_ADDR" );
if ( !remote_addr )
{
if ( !remote_addr ) {
Warning( "Can't determine remote address, using null" );
remote_addr = "";
}
@ -173,28 +155,25 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr )
char sql[ZM_SQL_SML_BUFSIZ] = "";
snprintf( sql, sizeof(sql), "SELECT Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds FROM Users WHERE Enabled = 1" );
if ( mysql_query( &dbconn, sql ) )
{
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
MYSQL_RES *result = mysql_store_result( &dbconn );
if ( !result )
{
if ( !result ) {
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
int n_users = mysql_num_rows( result );
if ( n_users < 1 )
{
if ( n_users < 1 ) {
mysql_free_result( result );
Warning( "Unable to authenticate user" );
return( 0 );
}
while( MYSQL_ROW dbrow = mysql_fetch_row( result ) )
{
while( MYSQL_ROW dbrow = mysql_fetch_row( result ) ) {
const char *user = dbrow[0];
const char *pass = dbrow[1];
@ -204,7 +183,7 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr )
unsigned char md5sum[md5len];
time_t now = time( 0 );
unsigned int hours =config.auth_hash_ttl;
unsigned int hours = config.auth_hash_ttl;
if ( ! hours ) {
Warning("No value set for ZM_AUTH_HASH_TTL. Defaulting to 2.");
@ -234,23 +213,23 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr )
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5data, md5sum, &md5len );
#endif
auth_md5[0] = '\0';
for ( unsigned int j = 0; j < md5len; j++ )
{
for ( unsigned int j = 0; j < md5len; j++ ) {
sprintf( &auth_md5[2*j], "%02x", md5sum[j] );
}
Debug( 1, "Checking auth_key '%s' -> auth_md5 '%s' == '%s'", auth_key, auth_md5, auth );
if ( !strcmp( auth, auth_md5 ) )
{
if ( !strcmp( auth, auth_md5 ) ) {
// We have a match
User *user = new User( dbrow );
Debug(1, "Authenticated user '%s'", user->getUsername() );
mysql_free_result( result );
return( user );
} else {
Debug(1, "No match for %s", auth );
}
}
}
mysql_free_result( result );
#else // HAVE_DECL_MD5
Error( "You need to build with gnutls or openssl installed to use hash based authentication" );
#endif // HAVE_DECL_MD5

View File

@ -259,23 +259,19 @@ fi
if [ $TYPE == "binary" ]; then
if [ "$INTERACTIVE" != "no" ]; then
read -p "Not doing dput since it's a binary release. Do you want to install it? (Y/N)"
read -p "Not doing dput since it's a binary release. Do you want to install it? (y/N)"
if [[ $REPLY == [yY] ]]; then
sudo dpkg -i $DIRECTORY*.deb
else
echo $REPLY;
fi;
if [ "$DISTRO" == "jessie" ]; then
read -p "Do you want to upload this binary to zmrepo? (y/N)"
if [[ $REPLY == [yY] ]]; then
if [ "$RELEASE" != "" ]; then
scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/stable/mini-dinstall/incoming/"
read -p "Do you want to upload this binary to zmrepo? (y/N)"
if [[ $REPLY == [yY] ]]; then
if [ "$RELEASE" != "" ]; then
scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/stable/mini-dinstall/incoming/"
else
if [ "$BRANCH" == "" ]; then
scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/master/mini-dinstall/incoming/"
else
if [ "$BRANCH" == "" ]; then
scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/master/mini-dinstall/incoming/"
else
scp "$DIRECTORY-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/${BRANCH}/mini-dinstall/incoming/"
fi;
scp "$DIRECTORY-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/${BRANCH}/mini-dinstall/incoming/"
fi;
fi;
fi;

View File

@ -1 +1 @@
1.31.2
1.31.3

View File

@ -94,9 +94,8 @@ switch ( $_REQUEST['task'] )
$sql .= " order by ".$sortField." ".$sortOrder." limit ".$limit;
$logs = array();
foreach ( dbFetchAll( $sql, NULL, $values ) as $log ) {
$log['DateTime'] = preg_replace( '/^\d+/', strftime( "%Y-%m-%d %H:%M:%S", intval($log['TimeKey']) ), $log['TimeKey'] );
$log['DateTime'] = preg_replace( '/^\d+/', strftime( '%Y-%m-%d %H:%M:%S', intval($log['TimeKey']) ), $log['TimeKey'] );
$log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : '';
#$log['Message'] = preg_replace('/[\x00-\x1F\x7F]/u', '', $log['Message'] );
$log['Message'] = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $log['Message'] );
$logs[] = $log;
}

View File

@ -101,49 +101,66 @@ CakeLog::config('debug', array(
'engine' => 'File',
'types' => array('notice', 'info', 'debug'),
'file' => 'cake_debug',
'path' => '@ZM_LOGDIR@/'
));
CakeLog::config('error', array(
'engine' => 'File',
'types' => array('warning', 'error', 'critical', 'alert', 'emergency'),
'file' => 'cake_error',
'path' => '@ZM_LOGDIR@/'
));
CakeLog::config('custom_path', array(
'engine' => 'File',
'path' => '@ZM_LOGDIR@'
'path' => '@ZM_LOGDIR@/'
));
Configure::write('ZM_CONFIG', '@ZM_CONFIG@');
Configure::write('ZM_CONFIG_SUBDIR', '@ZM_CONFIG_SUBDIR@');
Configure::write('ZM_VERSION', '@VERSION@');
Configure::write('ZM_API_VERSION', '@API_VERSION@');
loadConfigFile();
# Process name, value pairs from the main config file first
$configvals = process_configfile(Configure::read('ZM_CONFIG'));
function loadConfigFile() {
$configFile = Configure::read('ZM_CONFIG');
$localConfigFile = basename($configFile);
if ( file_exists( $localConfigFile ) && filesize( $localConfigFile ) > 0 )
{
if ( php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']) )
print( "Warning, overriding installed $localConfigFile file with local copy\n" );
else
error_log( "Warning, overriding installed $localConfigFile file with local copy" );
$configFile = $localConfigFile;
}
$cfg = fopen( $configFile, "r") or die("Could not open config file.");
while ( !feof($cfg) )
{
$str = fgets( $cfg, 256 );
if ( preg_match( '/^\s*$/', $str ))
continue;
elseif ( preg_match( '/^\s*#/', $str ))
continue;
elseif ( preg_match( '/^\s*([^=\s]+)\s*=\s*(.*?)\s*$/', $str, $matches )) {
Configure::write( $matches[1], $matches[2] );
define( $matches[1], $matches[2] );
}
}
fclose( $cfg );
# Search for user created config files. If one or more are found then
# update our config value array with those values
$configSubFolder = Configure::read('ZM_CONFIG_SUBDIR');
if ( is_dir($configSubFolder) ) {
if ( is_readable($configSubFolder) ) {
foreach ( glob("$configSubFolder/*.conf") as $filename ) {
$configvals = array_replace($configvals, process_configfile($filename) );
}
} else {
error_log( "WARNING: ZoneMinder configuration subfolder found but is not readable. Check folder permissions on $configSubFolder." );
}
}
# Now that our array our finalized, define each key => value
# pair in the array as a constant
foreach( $configvals as $key => $value) {
define( $key, $value );
Configure::write( $matches[1], $matches[2] );
}
function process_configfile($configFile) {
if ( is_readable( $configFile ) ) {
$configvals = array();
$cfg = fopen( $configFile, "r") or die("Could not open config file.");
while ( !feof($cfg) ) {
$str = fgets( $cfg, 256 );
if ( preg_match( '/^\s*$/', $str ))
continue;
elseif ( preg_match( '/^\s*#/', $str ))
continue;
elseif ( preg_match( '/^\s*([^=\s]+)\s*=\s*(.*?)\s*$/', $str, $matches ))
$configvals[$matches[1]] = $matches[2];
}
fclose( $cfg );
return( $configvals );
} else {
error_log( "WARNING: ZoneMinder configuration file found but is not readable. Check file permissions on $configFile." );
return( false );
}
}

View File

@ -1146,7 +1146,7 @@ function parseFilter( &$filter, $saveToSession=false, $querySep='&amp;' ) {
$StorageArea = NULL;
$terms = $filter['Query']['terms'];
$terms = isset($filter['Query']) ? $filter['Query']['terms'] : NULL;
if ( isset($terms) && count($terms) ) {
for ( $i = 0; $i < count($terms); $i++ ) {
@ -2133,18 +2133,14 @@ function getStreamHTML( $monitor, $options = array() ) {
$options['height'] = reScale( $monitor->Height(), $options['scale'] );
} else {
if ( ! isset( $options['width'] ) ) {
if ( $options['width'] == 100 ) {
$options['width'] = $monitor->Width();
} else {
$options['width'] = NULL;
}
} else if ( $options['width'] == 100 ) {
$options['width'] = $monitor->Width();
}
if ( ! isset( $options['height'] ) ) {
if ( $options['height'] == 100 ) {
$options['height'] = $monitor->Height();
} else {
$options['height'] = NULL;
}
} else if ( $options['height'] == 100 ) {
$options['height'] = $monitor->Height();
}
}
if ( ! isset($options['mode'] ) ) {

View File

@ -194,7 +194,7 @@ isset($view) || $view = NULL;
isset($request) || $request = NULL;
isset($action) || $action = NULL;
if ( ZM_ENABLE_CSRF_MAGIC && $action != 'login' && $view != 'view_video' ) {
if ( ZM_ENABLE_CSRF_MAGIC && $action != 'login' && $view != 'view_video' && $request != 'control' ) {
require_once( 'includes/csrf/csrf-magic.php' );
Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\"");
csrf_check();

View File

@ -30,6 +30,7 @@
#monitors .imageFeed img ,
#monitors .imageFeed svg {
border: 2px solid #ffffff;
display: block;
}
#monitors .imageFeed img.idle {

View File

@ -26,6 +26,7 @@
#monitors .imageFeed img ,
#monitors .imageFeed svg {
border: 2px solid #999999;
display: block;
}
#monitors .imageFeed img.idle {

View File

@ -69,7 +69,6 @@
#topPanel #textPanel {
text-align: left;
width: 100%;
height: 140px;
margin: 0 auto;
color: #dddddd;
font-size: 11px;

View File

@ -38,6 +38,7 @@
margin: 0;
padding: 0;
border-radius: 0;
display: block;
}
#monitors .imageFeed img.idle {

View File

@ -69,7 +69,6 @@
#topPanel #textPanel {
text-align: left;
width: 100%;
height: 140px;
margin: 0 auto;
color: #016A9D;
font-size: 11px;

View File

@ -211,10 +211,10 @@ function getNavBarHTML() {
<li><?php if ( logToDatabase() > Logger::NOLOG ) { ?> <?php echo makePopupLink( '?view=log', 'zmLog', 'log', '<span class="'.logState().'">'.translate('Log').'</span>' ) ?><?php } ?></li>
<?php } ?>
<?php if ( ZM_OPT_X10 && canView( 'Devices' ) ) { ?>
<li><a href="/?view=devices">Devices</a></li>
<li><a href="?view=devices">Devices</a></li>
<?php } ?>
<li><?php echo makePopupLink( '?view=groups', 'zmGroups', 'groups', sprintf( $CLANG['MonitorCount'], count($displayMonitors), zmVlang( $VLANG['Monitor'], count($displayMonitors) ) ).($group?' ('.$group['Name'].')':''), canView( 'Groups' ) ); ?></li>
<li><a href="/?view=filter">Filters</a></li>
<li><a href="?view=filter">Filters</a></li>
<?php
$cycleGroup = isset($_COOKIE['zmGroup'])?$_COOKIE['zmGroup']:0;
@ -285,4 +285,19 @@ function getNavBarHTML() {
<?php
return( ob_get_clean() );
} // end function getNavBarHTML()
function xhtmlFooter() {
global $view;
global $skin;
global $running;
if ( canEdit('System') ) {
include("skins/$skin/views/state.php");
?>
<?php
}
?>
</body></html>
<?php
} // end xhtmlFooter
?>

View File

@ -79,8 +79,8 @@ function getPopupSize( tag, width, height ) {
popupSize.height = height;
Error( "Got passed height but no addHeight when getting popup size for tag '"+tag+"'" );
}
if ( popupSize.minHeight && popupSize.height < popupSize.minHeight ) {
Warning( "Adjusting to minimum height when getting popup size for tag '"+tag+"'" );
if ( popupSize.minHeight && ( popupSize.height < popupSize.minHeight ) ) {
Warning( "Adjusting to minimum height ("+popupSize.minHeight+") when getting popup size for tag '"+tag+"' because calculated height is " + popupSize.height );
popupSize.height = popupSize.minHeight;
}
Debug( popupSize );
@ -293,4 +293,3 @@ function addVideoTimingTrack(video, LabelFormat, monitorName, duration, startTim
track.src = 'data:plain/text;charset=utf-8,'+encodeURIComponent(webvttdata);
video.appendChild(track);
}

View File

@ -290,11 +290,5 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
</tfoot>
</table>
</div>
</form>
<?php
if ( canEdit('System') ) {
include("skins/$skin/views/state.php");
}
?>
</body>
</html>
</form>
<?php xhtmlFooter() ?>

View File

@ -61,6 +61,7 @@ if ( isset( $_REQUEST['streamMode'] ) )
else
$streamMode = 'video';
$replayMode = '';
if ( isset( $_REQUEST['replayMode'] ) )
$replayMode = validHtmlStr($_REQUEST['replayMode']);
if ( isset( $_COOKIE['replayMode']) && preg_match('#^[a-z]+$#', $_COOKIE['replayMode']) )

View File

@ -405,5 +405,4 @@ if ( canEdit( 'Events' ) ) {
</form>
</div>
</div>
</body>
</html>
<?php xhtmlFooter() ?>

View File

@ -1,5 +1,5 @@
var logParms = "view=request&request=log&task=query";
var logReq = new Request.JSON( { url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: logResponse } );
var logReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: logResponse } );
var logTimer = undefined;
var logTable = undefined;

View File

@ -130,7 +130,7 @@ function selectLayout( element ) {
} else if ( streamImg.nodeName == 'APPLET' || streamImg.nodeName == 'OBJECT' ) {
// APPLET's and OBJECTS need to be re-initialized
}
streamImg.style.width = '';
streamImg.style.width = '100%';
}
var zonesSVG = $('zones'+monitor.id);
if ( zonesSVG ) {
@ -140,37 +140,8 @@ function selectLayout( element ) {
}
}
function changeWidth() {
function changeSize() {
var width = $('width').get('value');
for ( var x = 0; x < monitors.length; x++ ) {
var monitor = monitors[x];
/*Stream could be an applet so can't use moo tools*/
var streamImg = $( 'liveStream'+monitor.id );
if ( streamImg ) {
if ( streamImg.nodeName == 'IMG' ) {
var src = streamImg.src;
streamImg.src='';
src = src.replace(/width=[\.\d]+/i,'width='+width );
src = src.replace(/rand=\d+/i,'rand='+Math.floor((Math.random() * 1000000) ));
streamImg.src = src;
}
streamImg.style.width = width? width + "px" : null;
//streamImg.style.height = '';
}
var zonesSVG = $('zones'+monitor.id);
if ( zonesSVG ) {
zonesSVG.style.width = width ? width + "px" : '100%';
}
}
$('scale').set('value', '' );
Cookie.write( 'zmMontageScale', '', { duration: 10*365 } );
Cookie.write( 'zmMontageWidth', width, { duration: 10*365 } );
} // end function changeWidth()
function changeHeight() {
var height = $('height').get('value');
for ( var x = 0; x < monitors.length; x++ ) {
@ -181,21 +152,27 @@ function changeHeight() {
if ( streamImg.nodeName == 'IMG' ) {
var src = streamImg.src;
streamImg.src='';
src = src.replace(/width=[\.\d]+/i,'width='+width );
src = src.replace(/height=[\.\d]+/i,'height='+height );
src = src.replace(/rand=\d+/i,'rand='+Math.floor((Math.random() * 1000000) ));
streamImg.src = src;
streamImg.style.height = height ? height + "px" : null;
}
streamImg.style.width = width? width + "px" : null;
streamImg.style.height = height ? height + "px" : null;
//streamImg.style.height = '';
}
var zonesSVG = $('zones'+monitor.id);
if ( zonesSVG ) {
zonesSVG.style.width = width ? width + "px" : '100%';
zonesSVG.style.height = height + "px";
}
}
$('scale').set('value', '' );
Cookie.write( 'zmMontageHeight', height, { duration: 10*365 } );
Cookie.write( 'zmMontageScale', '', { duration: 10*365 } );
} // end function changeHeight()
Cookie.write( 'zmMontageWidth', width, { duration: 10*365 } );
Cookie.write( 'zmMontageHeight', height, { duration: 10*365 } );
} // end function changeSize()
function changeScale() {
var scale = $('scale').get('value');

View File

@ -32,7 +32,14 @@ $maxTimeSecs = strtotime('1950-01-01 01:01:01');
$index=0;
$anyAlarms=false;
foreach( dbFetchAll( $eventsSql ) as $event ) {
$result = dbQuery( $eventsSql );
if ( ! $result ) {
Fatal( "SQL-ERR");
return;
}
while( $event = $result->fetch( PDO::FETCH_ASSOC ) ) {
if ( $minTimeSecs > $event['StartTimeSecs'] ) $minTimeSecs = $event['StartTimeSecs'];
if ( $maxTimeSecs < $event['CalcEndTimeSecs'] ) $maxTimeSecs = $event['CalcEndTimeSecs'];
echo "
@ -84,8 +91,9 @@ $fromSecs=-1;
$toSecs=-1;
$maxScore=-1;
if ( $anyAlarms ) {
foreach( dbFetchAll ($frameSql) as $frame ) {
if ( $anyAlarms && $result = dbQuery( $frameSql ) ) {
while( $frame = $result->fetch( PDO::FETCH_ASSOC ) ) {
if ( $mId < 0 ) {
$mId = $frame['MonitorId'];
$fromSecs = $frame['TimeStampSecs'];

View File

@ -3,17 +3,14 @@ var filterQuery = '<?php echo validJsStr($filterQuery) ?>';
<?php
$jsMonitors = array();
$fields = array('Name', 'LabelFormat', 'SaveJPEGs', 'VideoWriter');
foreach ( $monitors as $monitor )
{
if ( !empty($monitorIds[$monitor['Id']]) )
{
$jsMonitor = array();
foreach ($fields as $field)
{
$jsMonitor[$field] = $monitor[$field];
}
$jsMonitors[$monitor['Id']] = $jsMonitor;
foreach ( $monitors as $monitor ) {
if ( !empty($monitorIds[$monitor['Id']]) ) {
$jsMonitor = array();
foreach ($fields as $field) {
$jsMonitor[$field] = $monitor[$field];
}
$jsMonitors[$monitor['Id']] = $jsMonitor;
}
}
?>
var monitors = <?php echo json_encode($jsMonitors) ?>;

View File

@ -58,7 +58,7 @@ if ( ! empty($_REQUEST['mid']) ) {
} else {
$nextId = getTableAutoInc( 'Monitors' );
if ( ! empty( $_REQUEST['dupId'] ) ) {
if ( isset( $_REQUEST['dupId'] ) ) {
$monitor = new Monitor( $_REQUEST['dupId'] );
if ( ZM_OPT_X10 )
$x10Monitor = dbFetchOne( 'SELECT * FROM TriggersX10 WHERE MonitorId = ?', NULL, array($_REQUEST['dupId']) );
@ -566,7 +566,7 @@ if ( $tab != 'source' || $monitor->Type()!= 'Remote' ) {
}
if ( $tab != 'source' || ($monitor->Type()!= 'Local' && $monitor->Type()!= 'Remote' && $monitor->Type()!= 'Ffmpeg' && $monitor->Type()!= 'Libvlc') ) {
?>
<input type="hidden" name="newMonitor[Method]" value="<?php echo validHtmlStr($monitor->Method()) ?>"/>
<input type="hidden" name="newMonitor[Method]" value="<?php echo validHtmlStr(null !== $monitor->Method() ? $monitor->Method() : 'rtpRtsp' ) ?>"/>
<?php
}
if ( $tab != 'source' || ($monitor->Type()!= 'Ffmpeg' && $monitor->Type()!= 'Libvlc' )) {

View File

@ -41,7 +41,6 @@ if ( isset( $_REQUEST['showZones'] ) ) {
}
}
$monitors = array();
<<<<<<< HEAD
$widths = array(
'' => 'auto',
160 => 160,
@ -68,7 +67,6 @@ if ( isset( $_REQUEST['scale'] ) ) {
if ( ! $scale )
$scale = 100;
}
foreach( dbFetchAll( $sql ) as $row ) {
if ( !visibleMonitor( $row['Id'] ) ) {
@ -142,8 +140,8 @@ if ( $showZones ) {
</div>
<h2><?php echo translate('Montage') ?></h2>
<div id="headerControl">
<span id="widthControl"><label><?php echo translate('Width') ?>:</label><?php echo htmlSelect( 'width', $widths, $options['width'], 'changeWidth(this);' ); ?></span>
<span id="heightControl"><label><?php echo translate('Height') ?>:</label><?php echo htmlSelect( 'height', $heights, $options['height'], 'changeHeight(this);' ); ?></span>
<span id="widthControl"><label><?php echo translate('Width') ?>:</label><?php echo htmlSelect( 'width', $widths, $options['width'], 'changeSize(this);' ); ?></span>
<span id="heightControl"><label><?php echo translate('Height') ?>:</label><?php echo htmlSelect( 'height', $heights, $options['height'], 'changeSize(this);' ); ?></span>
<span id="scaleControl"><label><?php echo translate('Scale') ?>:</label><?php echo htmlSelect( 'scale', $scales, $scale, 'changeScale(this);' ); ?></span>
<span id="layoutControl"><label for="layout"><?php echo translate('Layout') ?>:</label><?php echo htmlSelect( 'layout', $layouts, $layout, 'selectLayout(this);' )?></span>
</div>

View File

@ -116,7 +116,7 @@ if ( !empty($_REQUEST['group']) ) {
// Note we round up just a bit on the end time as otherwise you get gaps, like 59.78 to 00 in the next second, which can give blank frames when moved through slowly.
$eventsSql = '
SELECT E.Id,E.Name,E.StorageId,UNIX_TIMESTAMP(E.StartTime) AS StartTimeSecs,
SELECT E.Id,E.Name,UNIX_TIMESTAMP(E.StartTime) AS StartTimeSecs,
CASE WHEN E.EndTime IS NULL THEN (SELECT UNIX_TIMESTAMP(DATE_ADD(E.StartTime, Interval max(Delta)+0.5 Second)) FROM Frames F WHERE F.EventId=E.Id)
ELSE UNIX_TIMESTAMP(E.EndTime)
END AS CalcEndTimeSecs, E.Length,

View File

@ -34,8 +34,7 @@ require_once( $skinJsPhpFile );
<script type="text/javascript" src="<?php echo $skinJsFile ?>"></script>
<script type="text/javascript">
<?php
if ( !$debug )
{
if ( !$debug ) {
?>
closeWindow();
<?php

View File

@ -19,8 +19,8 @@
//
if ( !canEdit( 'Monitors' ) ) {
$view = 'error';
return;
$view = 'error';
return;
}
$cameras = array();
@ -40,78 +40,81 @@ function execONVIF( $cmd ) {
$html_output<br/><br/>
Please the following command from a command line for more information:<br/><br/>$shell_command"
);
} else {
Logger::Debug( "Results from probe: " . implode( '<br/>', $output ) );
}
return $output;
}
function probeCameras( $localIp ) {
$cameras = array();
$count = 0;
if ( $lines = @execONVIF( 'probe' ) ) {
foreach ( $lines as $line ) {
$line = rtrim( $line );
if ( preg_match( '|^(.+),(.+),\s\((.*)\)$|', $line, $matches ) ) {
$device_ep = $matches[1];
$soapversion = $matches[2];
$camera = array(
'model' => "Unknown ONVIF Camera",
'monitor' => array(
'Function' => "Monitor",
'Type' => 'Ffmpeg',
'Host' => $device_ep,
'SOAP' => $soapversion,
),
);
foreach ( preg_split('|,\s*|', $matches[3]) as $attr_val ) {
if( preg_match( '|(.+)=\'(.*)\'|', $attr_val, $tokens ) ) {
if($tokens[1] == "hardware") {
$camera['model'] = $tokens[2];
} elseif($tokens[1] == "name") {
$camera['monitor']['Name'] = $tokens[2];
} elseif($tokens[1] == "location") {
// $camera['location'] = $tokens[2];
}
}
}
$cameras[$count ++] = $camera;
$cameras = array();
if ( $lines = @execONVIF( 'probe' ) ) {
foreach ( $lines as $line ) {
$line = rtrim( $line );
if ( preg_match( '|^(.+),(.+),\s\((.*)\)$|', $line, $matches ) ) {
$device_ep = $matches[1];
$soapversion = $matches[2];
$camera = array(
'model' => 'Unknown ONVIF Camera',
'monitor' => array(
'Function' => 'Monitor',
'Type' => 'Ffmpeg',
'Host' => $device_ep,
'SOAP' => $soapversion,
),
);
foreach ( preg_split('|,\s*|', $matches[3]) as $attr_val ) {
if ( preg_match( '|(.+)=\'(.*)\'|', $attr_val, $tokens ) ) {
if ( $tokens[1] == 'hardware' ) {
$camera['model'] = $tokens[2];
} elseif ( $tokens[1] == 'name' ) {
$camera['monitor']['Name'] = $tokens[2];
} elseif ( $tokens[1] == 'location' ) {
// $camera['location'] = $tokens[2];
} else {
Logger::Debug('Unknown token ' . $tokens[1] );
}
}
}
return( $cameras );
}
} // end foreach token
$cameras[] = $camera;
}
} // end foreach line
}
return( $cameras );
}
function probeProfiles( $device_ep, $soapversion, $username, $password ) {
$profiles = array();
$count = 0;
if ( $lines = @execONVIF( "profiles $device_ep $soapversion $username $password" ) ) {
foreach ( $lines as $line ) {
$line = rtrim( $line );
if ( preg_match( '|^(.+),\s*(.+),\s*(.+),\s*(.+),\s*(.+),\s*(.+),\s*(.+)\s*$|', $line, $matches ) ) {
$stream_uri = $matches[7];
// add user@pass to URI
if( preg_match( '|^(\S+://)(.+)$|', $stream_uri, $tokens ) ) {
$stream_uri = $tokens[1].$username.':'.$password.'@'.$tokens[2];
}
$profile = array( # 'monitor' part of camera
'Type' => 'Ffmpeg',
'Width' => $matches[4],
'Height' => $matches[5],
'MaxFPS' => $matches[6],
'Path' => $stream_uri,
// local-only:
'Profile' => $matches[1],
'Name' => $matches[2],
'Encoding' => $matches[3],
);
$profiles[$count ++] = $profile;
}
}
$profiles = array();
if ( $lines = @execONVIF( "profiles $device_ep $soapversion $username $password" ) ) {
foreach ( $lines as $line ) {
$line = rtrim( $line );
if ( preg_match( '|^(.+),\s*(.+),\s*(.+),\s*(.+),\s*(.+),\s*(.+),\s*(.+)\s*$|', $line, $matches ) ) {
$stream_uri = $matches[7];
// add user@pass to URI
if ( preg_match( '|^(\S+://)(.+)$|', $stream_uri, $tokens ) ) {
$stream_uri = $tokens[1].$username.':'.$password.'@'.$tokens[2];
}
$profile = array( # 'monitor' part of camera
'Type' => 'Ffmpeg',
'Width' => $matches[4],
'Height' => $matches[5],
'MaxFPS' => $matches[6],
'Path' => $stream_uri,
// local-only:
'Profile' => $matches[1],
'Name' => $matches[2],
'Encoding' => $matches[3],
);
$profiles[] = $profile;
} else {
Logger::Debug("Line did not match preg: $line");
}
}
return( $profiles );
}
return( $profiles );
}

View File

@ -19,7 +19,7 @@
//
if ( !canEdit( 'System' ) ) {
$view = "error";
$view = 'error';
return;
}
?>
@ -40,18 +40,24 @@ if ( !canEdit( 'System' ) ) {
<label for="runState" class="col-sm-3 control-label">Change State</label>
<div class="col-sm-9">
<select id="runState" name="runState" class="form-control">
<?php if ( daemonCheck() ) { ?>
<?php
if ( $running ) {
?>
<option value="stop" selected="selected"><?php echo translate('Stop') ?></option>
<option value="restart"><?php echo translate('Restart') ?></option>
<?php } else { ?>
<option value="start" selected="selected"><?php echo translate('Start') ?></option>
<?php }
<?php
} else {
?>
<?php
<option value="start" selected="selected"><?php echo translate('Start') ?></option>
<?php
}
$states = dbFetchAll( 'SELECT * FROM States' );
foreach ( $states as $state ) { ?>
foreach ( $states as $state ) {
?>
<option value="<?php echo $state['Name'] ?>"><?php echo $state['Name'] ?></option>
<?php } ?>
<?php
}
?>
</select>
</div><!--col-sm-9-->
</div><!--form-group-->

View File

@ -37,16 +37,16 @@ define( 'STRF_TL_AXIS_RANGE_YEAR1', '%b %Y' );
define( 'STRF_TL_AXIS_RANGE_YEAR2', STRF_TL_AXIS_RANGE_YEAR1 );
// When the chart range is months
define( "STRF_TL_AXIS_RANGE_MONTH1", "%b" );
define( "STRF_TL_AXIS_RANGE_MONTH2", STRF_TL_AXIS_RANGE_MONTH1." %Y" );
define( 'STRF_TL_AXIS_RANGE_MONTH1', '%b' );
define( 'STRF_TL_AXIS_RANGE_MONTH2', STRF_TL_AXIS_RANGE_MONTH1.' %Y' );
// When the chart range is days
define( "STRF_TL_AXIS_RANGE_DAY1", "%d" );
define( "STRF_TL_AXIS_RANGE_DAY2", STRF_TL_AXIS_RANGE_DAY1." %b %Y" );
define( 'STRF_TL_AXIS_RANGE_DAY1', '%d' );
define( 'STRF_TL_AXIS_RANGE_DAY2', STRF_TL_AXIS_RANGE_DAY1.' %b %Y' );
// When the chart range is less than a day
define( "STRF_TL_AXIS_RANGE_TIME1", "%H:%M" );
define( "STRF_TL_AXIS_RANGE_TIME2", STRF_TL_AXIS_RANGE_TIME1.", %d %b %Y" );
define( 'STRF_TL_AXIS_RANGE_TIME1', '%H:%M' );
define( 'STRF_TL_AXIS_RANGE_TIME2', STRF_TL_AXIS_RANGE_TIME1.', %d %b %Y' );
//
// These are the time axis tick labels
@ -74,56 +74,56 @@ $minEventWidth = 3;
$maxEventWidth = 6;
$chart = array(
"width"=>700,
"height"=>460,
"image" => array(
"width"=>264,
"height"=>220,
"topOffset"=>20,
'width'=>700,
'height'=>460,
'image' => array(
'width'=>264,
'height'=>220,
'topOffset'=>20,
),
"imageText" => array(
"width"=>400,
"height"=>30,
"topOffset"=>20,
'imageText' => array(
'width'=>400,
'height'=>30,
'topOffset'=>20,
),
"graph" => array(
"width"=>600,
"height"=>160,
"topOffset"=>30,
'graph' => array(
'width'=>600,
'height'=>160,
'topOffset'=>30,
),
"title" => array(
"topOffset"=>50
'title' => array(
'topOffset'=>50
),
"key" => array(
"topOffset"=>50
'key' => array(
'topOffset'=>50
),
"axes" => array(
"x" => array(
"height" => 20,
'axes' => array(
'x' => array(
'height' => 20,
),
"y" => array(
"width" => 30,
'y' => array(
'width' => 30,
),
),
"grid" => array(
"x" => array(
"major" => array(
"max" => 12,
"min" => 4,
'grid' => array(
'x' => array(
'major' => array(
'max' => 12,
'min' => 4,
),
"minor" => array(
"max" => 48,
"min" => 12,
'minor' => array(
'max' => 48,
'min' => 12,
),
),
"y" => array(
"major" => array(
"max" => 8,
"min" => 1,
'y' => array(
'major' => array(
'max' => 8,
'min' => 1,
),
"minor" => array(
"max" => 0,
"min" => 0,
'minor' => array(
'max' => 0,
'min' => 0,
),
),
),
@ -152,7 +152,7 @@ if ( !empty($user['MonitorIds']) ) {
}
if ( isset($_REQUEST['filter']) )
$tree = parseFilterToTree( $_REQUEST['filter'] );
$tree = parseFilterToTree( $_REQUEST['filter']['Query'] );
else
$tree = false;
@ -253,16 +253,16 @@ if ( $tree ) {
}
$scales = array(
array( "name"=>"year", "factor"=>60*60*24*365, "align"=>1, "zoomout"=>2, "label"=>STRF_TL_AXIS_LABEL_YEAR ),
array( "name"=>"month", "factor"=>60*60*24*30, "align"=>1, "zoomout"=>12, "label"=>STRF_TL_AXIS_LABEL_MONTH ),
array( "name"=>"week", "factor"=>60*60*24*7, "align"=>1, "zoomout"=>4.25, "label"=>STRF_TL_AXIS_LABEL_WEEK, "labelCheck"=>"%W" ),
array( "name"=>"day", "factor"=>60*60*24, "align"=>1, "zoomout"=>7, "label"=>STRF_TL_AXIS_LABEL_DAY ),
array( "name"=>"hour4", "factor"=>60*60, "align"=>4, "zoomout"=>6, "label"=>STRF_TL_AXIS_LABEL_4HOUR, "labelCheck"=>"%H" ),
array( "name"=>"hour", "factor"=>60*60, "align"=>1, "zoomout"=>4, "label"=>STRF_TL_AXIS_LABEL_HOUR, "labelCheck"=>"%H" ),
array( "name"=>"minute10", "factor"=>60, "align"=>10, "zoomout"=>6, "label"=>STRF_TL_AXIS_LABEL_10MINUTE, "labelCheck"=>"%M" ),
array( "name"=>"minute", "factor"=>60, "align"=>1, "zoomout"=>10, "label"=>STRF_TL_AXIS_LABEL_MINUTE, "labelCheck"=>"%M" ),
array( "name"=>"second10", "factor"=>1, "align"=>10, "zoomout"=>6, "label"=>STRF_TL_AXIS_LABEL_10SECOND ),
array( "name"=>"second", "factor"=>1, "align"=>1, "zoomout"=>10, "label"=>STRF_TL_AXIS_LABEL_SECOND ),
array( 'name'=>"year", 'factor'=>60*60*24*365, 'align'=>1, 'zoomout'=>2, 'label'=>STRF_TL_AXIS_LABEL_YEAR ),
array( 'name'=>"month", 'factor'=>60*60*24*30, 'align'=>1, 'zoomout'=>12, 'label'=>STRF_TL_AXIS_LABEL_MONTH ),
array( 'name'=>"week", 'factor'=>60*60*24*7, 'align'=>1, 'zoomout'=>4.25, 'label'=>STRF_TL_AXIS_LABEL_WEEK, 'labelCheck'=>"%W" ),
array( 'name'=>"day", 'factor'=>60*60*24, 'align'=>1, 'zoomout'=>7, 'label'=>STRF_TL_AXIS_LABEL_DAY ),
array( 'name'=>"hour4", 'factor'=>60*60, 'align'=>4, 'zoomout'=>6, 'label'=>STRF_TL_AXIS_LABEL_4HOUR, 'labelCheck'=>"%H" ),
array( 'name'=>"hour", 'factor'=>60*60, 'align'=>1, 'zoomout'=>4, 'label'=>STRF_TL_AXIS_LABEL_HOUR, 'labelCheck'=>"%H" ),
array( 'name'=>"minute10", 'factor'=>60, 'align'=>10, 'zoomout'=>6, 'label'=>STRF_TL_AXIS_LABEL_10MINUTE, 'labelCheck'=>"%M" ),
array( 'name'=>"minute", 'factor'=>60, 'align'=>1, 'zoomout'=>10, 'label'=>STRF_TL_AXIS_LABEL_MINUTE, 'labelCheck'=>"%M" ),
array( 'name'=>"second10", 'factor'=>1, 'align'=>10, 'zoomout'=>6, 'label'=>STRF_TL_AXIS_LABEL_10SECOND ),
array( 'name'=>"second", 'factor'=>1, 'align'=>1, 'zoomout'=>10, 'label'=>STRF_TL_AXIS_LABEL_SECOND ),
);
$majXScale = getDateScale( $scales, $range, $chart['grid']['x']['major']['min'], $chart['grid']['x']['major']['max'] );
@ -291,17 +291,17 @@ if ( isset($minTime) && isset($maxTime) ) {
$eventsSql .= " and EndTime >= '$minTime' and StartTime <= '$maxTime'";
}
$eventsSql .= " order by Id asc";
$eventsSql .= ' order by Id asc';
//echo "ESQL: $eventsSql<br>";
$chart['data'] = array(
"x" => array(
"lo" => strtotime( $minTime ),
"hi" => strtotime( $maxTime ),
'x' => array(
'lo' => strtotime( $minTime ),
'hi' => strtotime( $maxTime ),
),
"y" => array(
"lo" => 0,
"hi" => 0,
'y' => array(
'lo' => 0,
'hi' => 0,
)
);
@ -311,95 +311,105 @@ $chart['data']['x']['density'] = $chart['data']['x']['range']/$chart['graph']['w
$monEventSlots = array();
$monFrameSlots = array();
$monitorIds = array();
foreach( dbFetchAll( $eventsSql ) as $event ) {
if ( !isset($monitorIds[$event['MonitorId']]) )
$monitorIds[$event['MonitorId']] = true;
$events_result = dbQuery( $eventsSql );
if ( ! $events_result ) {
Fatal( "SQL-ERR");
return;
}
if ( !isset($monEventSlots[$event['MonitorId']]) )
$monEventSlots[$event['MonitorId']] = array();
$first_event = $event = $events_result->fetch( PDO::FETCH_ASSOC );
if ( $event ) {
do {
if ( !isset($monFrameSlots[$event['MonitorId']]) )
$monFrameSlots[$event['MonitorId']] = array();
if ( !isset($monitorIds[$event['MonitorId']]) )
$monitorIds[$event['MonitorId']] = true;
$currEventSlots = &$monEventSlots[$event['MonitorId']];
$currFrameSlots = &$monFrameSlots[$event['MonitorId']];
if ( !isset($monEventSlots[$event['MonitorId']]) )
$monEventSlots[$event['MonitorId']] = array();
$startTimeT = strtotime($event['StartTime']);
$startIndex = $rawStartIndex = (int)(($startTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']);
if ( $startIndex < 0 )
$startIndex = 0;
if ( !isset($monFrameSlots[$event['MonitorId']]) )
$monFrameSlots[$event['MonitorId']] = array();
if ( isset($event['EndTime']) )
$endTimeT = strtotime($event['EndTime']);
else
$endTimeT = time();
$endIndex = $rawEndIndex = (int)(($endTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']);
$currEventSlots = &$monEventSlots[$event['MonitorId']];
$currFrameSlots = &$monFrameSlots[$event['MonitorId']];
if ( $endIndex >= $chart['graph']['width'] )
$endIndex = $chart['graph']['width'] - 1;
$startTimeT = strtotime($event['StartTime']);
$startIndex = $rawStartIndex = (int)(($startTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']);
if ( $startIndex < 0 )
$startIndex = 0;
for ( $i = $startIndex; $i <= $endIndex; $i++ ) {
if ( !isset($currEventSlots[$i]) ) {
if ( $rawStartIndex == $rawEndIndex ) {
$offset = 1;
} else {
$offset = 1 + ($event['Frames']?((int)(($event['Frames']-1)*(($i-$rawStartIndex)/($rawEndIndex-$rawStartIndex)))):0);
}
$currEventSlots[$i] = array( "count"=>0, "width"=>1, "offset"=>$offset, "event"=>$event );
} else {
$currEventSlots[$i]['count']++;
}
}
if ( $event['MaxScore'] > 0 ) {
if ( $startIndex == $endIndex ) {
$framesSql = 'SELECT FrameId,Score FROM Frames WHERE EventId = ? AND Score > 0 ORDER BY Score DESC LIMIT 1';
$frame = dbFetchOne( $framesSql, NULL, array($event['Id']) );
if ( isset($event['EndTime']) )
$endTimeT = strtotime($event['EndTime']);
else
$endTimeT = time();
$endIndex = $rawEndIndex = (int)(($endTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']);
$i = $startIndex;
if ( !isset($currFrameSlots[$i]) ) {
$currFrameSlots[$i] = array( 'count'=>1, 'value'=>$event['MaxScore'], 'event'=>$event, 'frame'=>$frame );
} else {
$currFrameSlots[$i]['count']++;
if ( $event['MaxScore'] > $currFrameSlots[$i]['value'] ) {
$currFrameSlots[$i]['value'] = $event['MaxScore'];
$currFrameSlots[$i]['event'] = $event;
$currFrameSlots[$i]['frame'] = $frame;
}
}
if ( $event['MaxScore'] > $chart['data']['y']['hi'] ) {
$chart['data']['y']['hi'] = $event['MaxScore'];
}
} else {
$framesSql = 'SELECT FrameId,Delta,unix_timestamp(TimeStamp) AS TimeT,Score FROM Frames WHERE EventId = ? AND Score > 0';
$result = dbQuery( $framesSql, array( $event['Id'] ) );
while( $frame = dbFetchNext( $result ) ) {
if ( $frame['Score'] == 0 )
continue;
$frameTimeT = $frame['TimeT'];
$frameTimeT = $startTimeT + $frame['Delta'];
$frameIndex = (int)(($frameTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']);
if ( $frameIndex < 0 )
continue;
if ( $frameIndex >= $chart['graph']['width'] )
continue;
if ( $endIndex >= $chart['graph']['width'] )
$endIndex = $chart['graph']['width'] - 1;
if ( !isset($currFrameSlots[$frameIndex]) ) {
$currFrameSlots[$frameIndex] = array( 'count'=>1, 'value'=>$frame['Score'], 'event'=>$event, 'frame'=>$frame );
for ( $i = $startIndex; $i <= $endIndex; $i++ ) {
if ( !isset($currEventSlots[$i]) ) {
if ( $rawStartIndex == $rawEndIndex ) {
$offset = 1;
} else {
$currFrameSlots[$frameIndex]['count']++;
if ( $frame['Score'] > $currFrameSlots[$frameIndex]['value'] ) {
$currFrameSlots[$frameIndex]['value'] = $frame['Score'];
$currFrameSlots[$frameIndex]['event'] = $event;
$currFrameSlots[$frameIndex]['frame'] = $frame;
$offset = 1 + ($event['Frames']?((int)(($event['Frames']-1)*(($i-$rawStartIndex)/($rawEndIndex-$rawStartIndex)))):0);
}
$currEventSlots[$i] = array( 'count'=>0, 'width'=>1, 'offset'=>$offset, 'event'=>$event );
} else {
$currEventSlots[$i]['count']++;
}
}
if ( $event['MaxScore'] > 0 ) {
if ( $startIndex == $endIndex ) {
$framesSql = 'SELECT FrameId,Score FROM Frames WHERE EventId = ? AND Score > 0 ORDER BY Score DESC LIMIT 1';
$frame = dbFetchOne( $framesSql, NULL, array($event['Id']) );
$i = $startIndex;
if ( !isset($currFrameSlots[$i]) ) {
$currFrameSlots[$i] = array( 'count'=>1, 'value'=>$event['MaxScore'], 'event'=>$event, 'frame'=>$frame );
} else {
$currFrameSlots[$i]['count']++;
if ( $event['MaxScore'] > $currFrameSlots[$i]['value'] ) {
$currFrameSlots[$i]['value'] = $event['MaxScore'];
$currFrameSlots[$i]['event'] = $event;
$currFrameSlots[$i]['frame'] = $frame;
}
}
if ( $frame['Score'] > $chart['data']['y']['hi'] ) {
$chart['data']['y']['hi'] = $frame['Score'];
if ( $event['MaxScore'] > $chart['data']['y']['hi'] ) {
$chart['data']['y']['hi'] = $event['MaxScore'];
}
} // end foreach frame
}
} // end if MaxScore > 0
} // end foreach event
} else {
$framesSql = 'SELECT FrameId,Delta,unix_timestamp(TimeStamp) AS TimeT,Score FROM Frames WHERE EventId = ? AND Score > 0';
$result = dbQuery( $framesSql, array( $event['Id'] ) );
while( $frame = dbFetchNext( $result ) ) {
if ( $frame['Score'] == 0 )
continue;
$frameTimeT = $frame['TimeT'];
$frameTimeT = $startTimeT + $frame['Delta'];
$frameIndex = (int)(($frameTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']);
if ( $frameIndex < 0 )
continue;
if ( $frameIndex >= $chart['graph']['width'] )
continue;
if ( !isset($currFrameSlots[$frameIndex]) ) {
$currFrameSlots[$frameIndex] = array( 'count'=>1, 'value'=>$frame['Score'], 'event'=>$event, 'frame'=>$frame );
} else {
$currFrameSlots[$frameIndex]['count']++;
if ( $frame['Score'] > $currFrameSlots[$frameIndex]['value'] ) {
$currFrameSlots[$frameIndex]['value'] = $frame['Score'];
$currFrameSlots[$frameIndex]['event'] = $event;
$currFrameSlots[$frameIndex]['frame'] = $frame;
}
}
if ( $frame['Score'] > $chart['data']['y']['hi'] ) {
$chart['data']['y']['hi'] = $frame['Score'];
}
} // end foreach frame
}
} // end if MaxScore > 0
} while( $event = $events_result->fetch( PDO::FETCH_ASSOC ) );
} # end if have first event
ksort( $monitorIds, SORT_NUMERIC );
ksort( $monEventSlots, SORT_NUMERIC );
@ -702,15 +712,17 @@ xhtmlHeaders(__FILE__, translate('Timeline') );
<div id="image" class="imageHeight">
<img id="imageSrc" class="imageWidth" src="graphics/transparent.gif" alt="<?php echo translate('ViewEvent') ?>" title="<?php echo translate('ViewEvent') ?>"/>
<?php
if ( 0 ) {
//due to chrome bug, has to enable https://code.google.com/p/chromium/issues/detail?id=472300
//crossorigin has to be added below to make caption work in chrome
?>
<!--
<video id="preview" width="100%" controls crossorigin="anonymous">
<source src="<?php echo getEventDefaultVideoPath($event); ?>" type="video/mp4">
<source src="<?php echo getEventDefaultVideoPath($first_event); ?>" type="video/mp4">
Your browser does not support the video tag.
</video>
o-->
<?php } ?>
</div>
</div>
@ -807,16 +819,12 @@ foreach( array_keys($monEventSlots) as $monitorId ) {
<?php
unset( $currEventSlots );
$currEventSlots = &$monEventSlots[$monitorId];
$monitorMouseover = $mouseover;
if ($monitors[$monitorId]['SaveJPEGs'] == 2) {
$monitorMouseover = false;
}
for ( $i = 0; $i < $chart['graph']['width']; $i++ ) {
if ( isset($currEventSlots[$i]) ) {
unset( $slot );
$slot = &$currEventSlots[$i];
if ( $monitorMouseover ) {
if ( $mouseover ) {
$behaviours = array(
'onclick="'.getSlotShowEventBehaviour( $slot ).'"',
'onmouseover="'.getSlotPreviewEventBehaviour( $slot ).'"'
@ -854,6 +862,6 @@ foreach( array_keys($monEventSlots) as $monitorId ) {
</div>
</div>
</div>
<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.6/moment.min.js"></script>
<script src="skins/<?php echo $skin ?>/js/moment.min.js"></script>
</body>
</html>