diff --git a/.travis.yml b/.travis.yml index 8ab86bf7d..7cfe96854 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,24 +33,23 @@ install: env: - SMPFLAGS=-j4 OS=el DIST=7 - - SMPFLAGS=-j4 OS=el DIST=8 - - SMPFLAGS=-j4 OS=fedora DIST=29 DOCKER_REPO=knnniggett/packpack + - SMPFLAGS=-j4 OS=el DIST=8 DOCKER_REPO=knnniggett/packpack - SMPFLAGS=-j4 OS=fedora DIST=30 - - SMPFLAGS=-j4 OS=ubuntu DIST=trusty DOCKER_REPO=iconzm/packpack USE_SFTP=yes - - SMPFLAGS=-j4 OS=ubuntu DIST=xenial DOCKER_REPO=iconzm/packpack USE_SFTP=yes - - SMPFLAGS=-j4 OS=ubuntu DIST=bionic DOCKER_REPO=iconzm/packpack USE_SFTP=yes - - SMPFLAGS=-j4 OS=ubuntu DIST=disco DOCKER_REPO=iconzm/packpack USE_SFTP=yes - - SMPFLAGS=-j4 OS=ubuntu DIST=eoan DOCKER_REPO=iconzm/packpack USE_SFTP=yes - - SMPFLAGS=-j4 OS=debian DIST=jessie DOCKER_REPO=iconzm/packpack USE_SFTP=yes - - SMPFLAGS=-j4 OS=debian DIST=stretch DOCKER_REPO=iconzm/packpack USE_SFTP=yes - - SMPFLAGS=-j4 OS=debian DIST=buster DOCKER_REPO=iconzm/packpack USE_SFTP=yes + - SMPFLAGS=-j4 OS=fedora DIST=31 + - SMPFLAGS=-j4 OS=ubuntu DIST=trusty DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=ubuntu DIST=xenial DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=ubuntu DIST=bionic DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=ubuntu DIST=disco DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=ubuntu DIST=eoan DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=debian DIST=jessie DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=debian DIST=stretch DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=debian DIST=buster DOCKER_REPO=iconzm/packpack - SMPFLAGS=-j4 OS=ubuntu DIST=trusty ARCH=i386 - SMPFLAGS=-j4 OS=ubuntu DIST=xenial ARCH=i386 - - SMPFLAGS=-j4 OS=ubuntu DIST=bionic ARCH=i386 - SMPFLAGS=-j4 OS=ubuntu DIST=disco ARCH=i386 - SMPFLAGS=-j4 OS=debian DIST=buster ARCH=i386 - SMPFLAGS=-j4 OS=debian DIST=stretch ARCH=i386 - - SMPFLAGS=-j4 OS=raspbian DIST=stretch ARCH=armhf DOCKER_REPO=knnniggett/packpack + - SMPFLAGS=-j4 OS=eslint DIST=eslint compiler: - gcc @@ -58,12 +57,6 @@ services: - mysql - docker -jobs: - include: - - name: eslint - install: npm install -g eslint@5.12.0 eslint-config-google@0.11.0 eslint-plugin-html@5.0.0 eslint-plugin-php-markup@0.2.5 - script: eslint --ext .php,.js . - script: - utils/packpack/startpackpack.sh diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 44eb1a048..c9b2b6ea1 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -434,6 +434,7 @@ DROP TABLE IF EXISTS `Monitors`; CREATE TABLE `Monitors` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', + `Notes` TEXT, `ServerId` int(10) unsigned, `StorageId` smallint(5) unsigned default 0, `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL default 'Local', diff --git a/db/zm_update-1.33.16.sql b/db/zm_update-1.33.16.sql new file mode 100644 index 000000000..87058e3f8 --- /dev/null +++ b/db/zm_update-1.33.16.sql @@ -0,0 +1,12 @@ + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'Notes' + ) > 0, + "SELECT 'Column Notes already exists in Monitors'", + "ALTER TABLE `Monitors` ADD `Notes` TEXT AFTER `Name`" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/distros/redhat/readme/README b/distros/redhat/readme/README index 0b3e4bb9c..f0fff828f 100644 --- a/distros/redhat/readme/README +++ b/distros/redhat/readme/README @@ -22,14 +22,10 @@ What's New switching between httpd <-> nginx requires manaully changing ownership of all event folders and the php session folder after the change. -4. If you have installed ZoneMinder from the FedBerry repositories, this build - of ZoneMinder has support for Raspberry Pi hardware acceleration when using - ffmpeg. Unforunately, there is a problem with the same hardware acceleration - when using libvlc. Consequently, libvlc support in this build of ZoneMinder - has been disabled until the problem is resolved. See the following bug - report for details: https://trac.videolan.org/vlc/ticket/18594 +4. The timezone must now be set from the ZoneMinder web console. See the + appropriate README, mentioned in the next step, for details. -5. Continue on to the next README that corresponds to the chosen webserver: +6. Continue on to the next README that corresponds to your chosen webserver: README.httpd - Follow these steps when using Apache README.nginx - Follow these steps when using Nginx diff --git a/distros/redhat/readme/README.httpd b/distros/redhat/readme/README.httpd index 5301850df..1ba0ee688 100644 --- a/distros/redhat/readme/README.httpd +++ b/distros/redhat/readme/README.httpd @@ -36,20 +36,17 @@ NOTE: EL7 users should replace "dnf" with "yum" in the instructions below. sudo chown root:apache *.conf sudo chmod 640 *.conf -4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local - timezone. PHP will complain loudly if this is not set, or if it is set - incorrectly, and these complaints will show up in the zoneminder logging - system as errors. +4. Manually setting the timezone in /etc/php.ini is deprecated. - If you are not sure of the proper timezone specification to use, look at - http://php.net/date.timezone + Instead, navigate to Options -> System from the ZoneMinder web console. + Do this after completing step 10, below. + + Note that timezone errors will appear in the ZoneMinder log until this + has been completed. 5. Disable SELinux - We currently do not have the resources to create and maintain an accurate - SELinux policy for ZoneMinder on Fedora. We will gladly accept pull - reqeusts from anyone who wishes to do the work. In the meantime, SELinux - will need to be disabled or put into permissive mode. + SELinux must be disabled or put into permissive mode. This is not optional! To immediately disbale SELinux for the current seesion, issue the following from the command line: @@ -166,3 +163,11 @@ Upgrades sudo systemctl restart httpd sudo systemctl start zoneminder +6. Manually setting the timezone in /etc/php.ini is deprecated. + + Instead, navigate to Options -> System from the ZoneMinder web console. + Do this now. + + Note that timezone errors will appear in the ZoneMinder log until this + has been completed. + diff --git a/distros/redhat/readme/README.nginx b/distros/redhat/readme/README.nginx index cca4e72c2..8bc3bbdc1 100644 --- a/distros/redhat/readme/README.nginx +++ b/distros/redhat/readme/README.nginx @@ -34,13 +34,13 @@ New installs sudo chown root:nginx *.conf sudo chmod 640 *.conf -4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local - timezone. PHP will complain loudly if this is not set, or if it is set - incorrectly, and these complaints will show up in the zoneminder logging - system as errors. +4. Manually setting the timezone in /etc/php.ini is deprecated. - If you are not sure of the proper timezone specification to use, look at - http://php.net/date.timezone + Instead, navigate to Options -> System from the ZoneMinder web console. + Do this after completing step 10, below. + + Note that timezone errors will appear in the ZoneMinder log until this + has been completed. 5. Disable SELinux @@ -169,3 +169,11 @@ Upgrades sudo systemctl restart php-fpm sudo systemctl start zoneminder +6. Manually setting the timezone in /etc/php.ini is deprecated. + + Instead, navigate to Options -> System from the ZoneMinder web console. + Do this now. + + Note that timezone errors will appear in the ZoneMinder log until this + has been completed. + diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index e319f4c8f..6715f7b26 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -14,16 +14,21 @@ # This will tell zoneminder's cmake process we are building against a known distro %global zmtargetdistro %{?rhel:el%{rhel}}%{!?rhel:fc%{fedora}} -# Fedora >= 25 needs apcu backwards compatibility module -%if 0%{?fedora} >= 25 +# Fedora needs apcu backwards compatibility module +%if 0%{?fedora} %global with_apcu_bc 1 %endif +# Newer php's keep json functions in a subpackage +%if 0%{?fedora} || 0%{?rhel} >= 8 +%global with_php_json 1 +%endif + # The default for everything but el7 these days %global _hardened_build 1 Name: zoneminder -Version: 1.33.14 +Version: 1.33.16 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons @@ -105,7 +110,7 @@ Summary: Common files for ZoneMinder, not tied to a specific web server Requires: php-mysqli Requires: php-common Requires: php-gd -%{?fedora:Requires: php-json} +%{?with_php_json:Requires: php-json} Requires: php-pecl-apcu %{?with_apcu_bc:Requires: php-pecl-apcu-bc} Requires: cambozola @@ -411,6 +416,9 @@ EOF %dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload %changelog +* Sun Dec 08 2019 Isaac Connor - 1.33.15-1 +- Bump to 1.33.15 Development + * Sun Aug 11 2019 Andrew Bauer - 1.33.14-1 - Bump to 1.33.13 Development diff --git a/distros/ubuntu1604/control b/distros/ubuntu1604/control index 7e57c98a8..1b7b96320 100644 --- a/distros/ubuntu1604/control +++ b/distros/ubuntu1604/control @@ -74,7 +74,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,libfile-slurp-perl ,mysql-client | mariadb-client | virtual-mysql-client ,perl-modules - ,php5-mysql | php-mysql, php5-gd | php-gd , php5-apcu | php-apcu , php-apc | php-apcu-bc + ,php5-mysql | php-mysql, php5-gd | php-gd , php5-apcu | php-apcu , php-apc | php-apcu-bc, php-json ,policykit-1 ,rsyslog | system-log-daemon ,zip diff --git a/misc/logcheck b/misc/logcheck index e59948e86..820108a66 100644 --- a/misc/logcheck +++ b/misc/logcheck @@ -28,3 +28,4 @@ zmtelemetry\[[[:digit:]]+\]: INF \[Telemetry data uploaded successfully.\]$ zmtelemetry\[[[:digit:]]+\]: INF \[Sending data to ZoneMinder Telemetry server.\]$ zmtelemetry\[[[:digit:]]+\]: INF \[Collec?ting data to send to ZoneMinder Telemetry server.\]$ web_php\[[[:digit:]]+\]: INF \[Login successful for user "[[:alnum:]]+"\]$ +zmeventnotification\[[[:digit:]]+\]: INF diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in index 31fafc1bc..cd93dfe15 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in @@ -1127,7 +1127,7 @@ our @options = ( }, { name => 'ZM_LOG_LEVEL_FILE', - default => '-5', + default => '1', description => 'Save logging output to component files', help => q` ZoneMinder logging is now more integrated between @@ -1312,7 +1312,7 @@ our @options = ( }, { name => 'ZM_LOG_DEBUG_FILE', - default => '@ZM_LOGDIR@/zm_debug.log+', + default => '', description => 'Where extra debug is output to', help => q` This option allows you to specify a different target for debug @@ -2468,7 +2468,7 @@ our @options = ( }, { name => 'ZM_WATCH_MAX_DELAY', - default => '5', + default => '45', description => 'The maximum delay allowed since the last captured image', help => q` The zmwatch daemon checks the image capture performance of the diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/Amcrest_HTTP.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/Amcrest_HTTP.pm index e95f86cba..3fd36a0c4 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/Amcrest_HTTP.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/Amcrest_HTTP.pm @@ -1,6 +1,6 @@ # ========================================================================== # -# ZoneMinder Acrest HTTP API Control Protocol Module, 20180214, Rev 3.0 +# ZoneMinder Amcrest HTTP API Control Protocol Module, 20180214, Rev 3.0 # # Change Log # @@ -8,7 +8,7 @@ # - Fixes incorrect method names # - Updates control sequences to Amcrest HTTP Protocol API v 2.12 # - Extends control features -# +# # Rev 2.0: # - Fixed installation instructions text, no changes to functionality. # @@ -38,6 +38,7 @@ use Time::HiRes qw( usleep ); require ZoneMinder::Base; require ZoneMinder::Control; +require LWP::UserAgent; our @ISA = qw(ZoneMinder::Control); @@ -50,130 +51,109 @@ our @ISA = qw(ZoneMinder::Control); use ZoneMinder::Logger qw(:all); use ZoneMinder::Config qw(:all); -sub new -{ - my $class = shift; - my $id = shift; - my $self = ZoneMinder::Control->new( $id ); - bless( $self, $class ); - srand( time() ); - return $self; +sub new { + my $class = shift; + my $id = shift; + my $self = ZoneMinder::Control->new($id); + bless($self, $class); + return $self; } -our $AUTOLOAD; +sub open { + my $self = shift; -sub AUTOLOAD -{ - my $self = shift; - my $class = ref($self) || croak( "$self not object" ); - my $name = $AUTOLOAD; - $name =~ s/.*://; - Debug( "Received command: $name" ); - if ( exists($self->{$name}) ) - { - return( $self->{$name} ); - } - Fatal( "Can't access $name member of object of class $class" ); -} + $self->loadMonitor(); + my $username; + my $password; + my $realm = 'Login to ' . $self->{Monitor}->{ControlDevice}; -sub open -{ - my $self = shift; + if ( $self->{Monitor}->{ControlAddress} =~ /(.*):(.*)@(.*)/ ) { + $username = $1; + $password = $2; + $$self{address} = $3; + } - $self->loadMonitor(); + $self->{ua} = LWP::UserAgent->new; + $self->{ua}->credentials($$self{address}, $realm, $username, $password); + $self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION); + + # Detect REALM + my $res = $self->{ua}->get($$self{address}.'/cgi-bin/ptz.cgi'); + + if ( $res->is_success ) { $self->{state} = 'open'; -} + return; + } -sub initUA -{ - my $self = shift; - my $user = undef; - my $password = undef; - my $address = undef; + if ( $res->status_line() eq '401 Unauthorized' ) { - if ( $self->{Monitor}->{ControlAddress} =~ /(.*):(.*)@(.*)/ ) - { - $user = $1; - $password = $2; - $address = $3; + my $headers = $res->headers(); + foreach my $k ( keys %$headers ) { + Debug("Initial Header $k => $$headers{$k}"); } - use LWP::UserAgent; - $self->{ua} = LWP::UserAgent->new; - $self->{ua}->credentials("$address", "Login to " . $self->{Monitor}->{ControlDevice}, "$user", "$password"); - $self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION ); + if ( $$headers{'www-authenticate'} ) { + my ( $auth, $tokens ) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/; + if ( $tokens =~ /\w+="([^"]+)"/i ) { + if ( $realm ne $1 ) { + $realm = $1; + Debug("Changing REALM to $realm"); + $self->{ua}->credentials($$self{address}, $realm, $username, $password); + $res = $self->{ua}->get($$self{address}.'/cgi-bin/ptz.cgi'); + if ( $res->is_success() ) { + $self->{state} = 'open'; + return; + } + Error('Authentication still failed after updating REALM' . $res->status_line); + $headers = $res->headers(); + foreach my $k ( keys %$headers ) { + Debug("Initial Header $k => $$headers{$k}"); + } # end foreach + } else { + Error('Authentication failed, not a REALM problem'); + } + } else { + Error('Failed to match realm in tokens'); + } # end if + } else { + Debug('No headers line'); + } # end if headers + } # end if $res->status_line() eq '401 Unauthorized' + + $self->{state} = 'open'; } -sub destroyUA -{ - my $self = shift; - - $self->{ua} = undef; +sub close { + my $self = shift; + $self->{state} = 'closed'; } -sub close -{ - my $self = shift; - $self->{state} = 'closed'; +sub sendCmd { + my $self = shift; + my $cmd = shift; + my $result = undef; + + $self->printMsg($cmd, 'Tx'); + + my $req = HTTP::Request->new( GET=>"http://$$self{address}/$cmd" ); + my $res = $self->{ua}->request($req); + + if ( $res->is_success ) { + $result = !undef; + # Command to camera appears successful, write Info item to log + Info('Camera control: \''.$res->status_line().'\' for URL '.$self->{Monitor}->{ControlAddress}.'/'.$cmd); + # TODO: Add code to retrieve $res->message_decode or some such. Then we could do things like check the camera status. + } else { + Error('Camera control command FAILED: \''.$res->status_line().'\' for URL '.$self->{Monitor}->{ControlAddress}.'/'.$cmd); + } + + return $result; } -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; - - destroyUA($self); - initUA($self); - - my $user = undef; - my $password = undef; - my $address = undef; - - if ( $self->{Monitor}->{ControlAddress} =~ /(.*):(.*)@(.*)/ ) - { - $user = $1; - $password = $2; - $address = $3; - } - - printMsg( $cmd, "Tx" ); - - my $req = HTTP::Request->new( GET=>"http://$address/$cmd" ); - my $res = $self->{ua}->request($req); - - if ( $res->is_success ) - { - $result = !undef; - # Command to camera appears successful, write Info item to log - Info( "Camera control: '".$res->status_line()."' for URL ".$self->{Monitor}->{ControlAddress}."/$cmd" ); - # TODO: Add code to retrieve $res->message_decode or some such. Then we could do things like check the camera status. - } - else - { - Error( "Camera control command FAILED: '".$res->status_line()."' for URL ".$self->{Monitor}->{ControlAddress}."/$cmd" ); - } - - return( $result ); -} - -sub reset -{ - my $self = shift; - # This reboots the camera effectively resetting it - Debug( "Camera Reset" ); - $self->sendCmd( 'cgi-bin/magicBox.cgi?action=reboot' ); - ##FIXME: Exit is a bad idea as it appears to cause zmc to run away. - #Exit (0); +sub reset { + my $self = shift; + # This reboots the camera effectively resetting it + $self->sendCmd('cgi-bin/magicBox.cgi?action=reboot'); } # NOTE: I'm putting this in, but absolute camera movement does not seem to be well supported in the classic skin ATM. @@ -184,163 +164,148 @@ sub reset sub moveAbs ## Up, Down, Left, Right, etc. ??? Doesn't make sense here... { - my $self = shift; - my $pan_degrees = shift || 0; - my $tilt_degrees = shift || 0; - my $speed = shift || 1; - Debug( "Move ABS" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1='.$pan_degrees.'&arg2='.$tilt_degrees.'&arg3=0&arg4='.$speed ); + my $self = shift; + my $pan_degrees = shift || 0; + my $tilt_degrees = shift || 0; + my $speed = shift || 1; + Debug('Move ABS'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1='.$pan_degrees.'&arg2='.$tilt_degrees.'&arg3=0&arg4='.$speed); } -sub moveConUp -{ - my $self = shift; - Debug( "Move Up" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=Up&channel=0&arg1=0&arg2=1&arg3=0' ); - usleep (500); ##XXX Should this be passed in as a "speed" parameter? - $self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=Up&channel=0&arg1=0&arg2=1&arg3=0' ); +sub moveConUp { + my $self = shift; + Debug('Move Up'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=Up&channel=0&arg1=0&arg2=1&arg3=0'); + usleep(500); ##XXX Should this be passed in as a "speed" parameter? + $self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=Up&channel=0&arg1=0&arg2=1&arg3=0'); } -sub moveConDown -{ - my $self = shift; - Debug( "Move Down" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=Down&channel=0&arg1=0&arg2=1&arg3=0' ); - usleep (500); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=Down&channel=0&arg1=0&arg2=1&arg3=0' ); +sub moveConDown { + my $self = shift; + Debug('Move Down'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=Down&channel=0&arg1=0&arg2=1&arg3=0'); + usleep(500); + $self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=Down&channel=0&arg1=0&arg2=1&arg3=0'); } -sub moveConLeft -{ - my $self = shift; - Debug( "Move Left" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=Left&channel=0&arg1=0&arg2=1&arg3=0' ); - usleep (500); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=Left&channel=0&arg1=0&arg2=1&arg3=0' ); +sub moveConLeft { + my $self = shift; + Debug('Move Left'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=Left&channel=0&arg1=0&arg2=1&arg3=0'); + usleep(500); + $self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=Left&channel=0&arg1=0&arg2=1&arg3=0'); } -sub moveConRight -{ - my $self = shift; - Debug( "Move Right" ); -# $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1=270&arg2=5&arg3=0' ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=Right&channel=0&arg1=0&arg2=1&arg3=0' ); - usleep (500); - Debug( "Move Right Stop" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=Right&channel=0&arg1=0&arg2=1&arg3=0' ); +sub moveConRight { + my $self = shift; + Debug('Move Right'); + # $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1=270&arg2=5&arg3=0' ); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=Right&channel=0&arg1=0&arg2=1&arg3=0'); + usleep(500); + Debug('Move Right Stop'); + $self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=Right&channel=0&arg1=0&arg2=1&arg3=0'); } -sub moveConUpRight -{ - my $self = shift; - Debug( "Move Diagonally Up Right" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=RightUp&channel=0&arg1=1&arg2=1&arg3=0' ); - usleep (500); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=RightUp&channel=0&arg1=0&arg2=1&arg3=0' ); +sub moveConUpRight { + my $self = shift; + Debug('Move Diagonally Up Right'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=RightUp&channel=0&arg1=1&arg2=1&arg3=0'); + usleep(500); + $self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=RightUp&channel=0&arg1=0&arg2=1&arg3=0'); } -sub moveConDownRight -{ - my $self = shift; - Debug( "Move Diagonally Down Right" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=RightDown&channel=0&arg1=1&arg2=1&arg3=0' ); - usleep (500); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=RightDown&channel=0&arg1=0&arg2=1&arg3=0' ); +sub moveConDownRight { + my $self = shift; + Debug('Move Diagonally Down Right'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=RightDown&channel=0&arg1=1&arg2=1&arg3=0'); + usleep(500); + $self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=RightDown&channel=0&arg1=0&arg2=1&arg3=0'); } -sub moveConUpLeft -{ - my $self = shift; - Debug( "Move Diagonally Up Left" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=LeftUp&channel=0&arg1=1&arg2=1&arg3=0' ); - usleep (500); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=LeftUp&channel=0&arg1=0&arg2=1&arg3=0' ); +sub moveConUpLeft { + my $self = shift; + Debug('Move Diagonally Up Left'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=LeftUp&channel=0&arg1=1&arg2=1&arg3=0'); + usleep(500); + $self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=LeftUp&channel=0&arg1=0&arg2=1&arg3=0'); } -sub moveConDownLeft -{ - my $self = shift; - Debug( "Move Diagonally Down Left" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=LeftDown&channel=0&arg1=1&arg2=1&arg3=0' ); - usleep (500); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=LeftDown&channel=0&arg1=0&arg2=1&arg3=0' ); +sub moveConDownLeft { + my $self = shift; + Debug('Move Diagonally Down Left'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=LeftDown&channel=0&arg1=1&arg2=1&arg3=0'); + usleep (500); + $self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=LeftDown&channel=0&arg1=0&arg2=1&arg3=0'); } # Stop is not "correctly" implemented as control_functions.php translates this to "Center" # So we'll just send the camera to 0* Horz, 0* Vert, zoom out; Also, Amcrest does not seem to # support a generic stop-all-current-action command. -sub moveStop -{ - my $self = shift; - Debug( "Move Stop/Center" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1=0&arg2=0&arg3=0&arg4=1' ); +sub moveStop { + my $self = shift; + Debug('Move Stop/Center'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1=0&arg2=0&arg3=0&arg4=1'); } # Move Camera to Home Position # The current API does not support a Home per se, so we'll just send the camera to preset #1 # NOTE: It goes without saying that the user must have set up preset #1 for this to work. -sub presetHome -{ - my $self = shift; - Debug( "Home Preset" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&channel=0&code=GotoPreset&&arg1=0&arg2=1&arg3=0&arg4=0' ); +sub presetHome { + my $self = shift; + Debug('Home Preset'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=GotoPreset&&arg1=0&arg2=1&arg3=0&arg4=0'); } -sub presetGoto -{ - my $self = shift; - my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Go To Preset $preset" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&channel=0&code=GotoPreset&&arg1=0&arg2='.$preset.'&arg3=0&arg4=0' ); +sub presetGoto { + my $self = shift; + my $params = shift; + my $preset = $self->getParam($params, 'preset'); + Debug("Go To Preset $preset"); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=GotoPreset&&arg1=0&arg2='.$preset.'&arg3=0&arg4=0'); } -sub presetSet -{ - my $self = shift; - my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Set Preset" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&channel=0&code=SetPreset&arg1=0&arg2='.$preset.'&arg3=0&arg4=0' ); +sub presetSet { + my $self = shift; + my $params = shift; + my $preset = $self->getParam($params, 'preset'); + Debug('Set Preset'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=SetPreset&arg1=0&arg2='.$preset.'&arg3=0&arg4=0'); } # NOTE: This does not appear to be implemented in the classic skin. But we'll leave it here for later. -sub moveMap -{ - my $self = shift; - my $params = shift; +sub moveMap { + my $self = shift; + my $params = shift; - my $xcoord = $self->getParam( $params, 'xcoord', $self->{Monitor}{Width}/2 ); - my $ycoord = $self->getParam( $params, 'ycoord', $self->{Monitor}{Height}/2 ); - # if the camera is mounted upside down, you may have to inverse these coordinates - # just use 360 minus pan instead of pan, 90 minus tilt instead of tilt - # Convert xcoord into pan position 0 to 359 - my $pan = int(360 * $xcoord / $self->{Monitor}{Width}); - # Convert ycoord into tilt position 0 to 89 - my $tilt = 90 - int(90 * $ycoord / $self->{Monitor}{Height}); - # Now get the following url: - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1='.$pan.'&arg2='.$tilt.'&arg3=1&arg4=1'); + my $xcoord = $self->getParam( $params, 'xcoord', $self->{Monitor}{Width}/2 ); + my $ycoord = $self->getParam( $params, 'ycoord', $self->{Monitor}{Height}/2 ); + # if the camera is mounted upside down, you may have to inverse these coordinates + # just use 360 minus pan instead of pan, 90 minus tilt instead of tilt + # Convert xcoord into pan position 0 to 359 + my $pan = int(360 * $xcoord / $self->{Monitor}{Width}); + # Convert ycoord into tilt position 0 to 89 + my $tilt = 90 - int(90 * $ycoord / $self->{Monitor}{Height}); + # Now get the following url: + $self->sendCmd('cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1='.$pan.'&arg2='.$tilt.'&arg3=1&arg4=1'); } -sub zoomConTele -{ - my $self = shift; - Debug( "Zoom continuous tele" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&channel=0&code=ZoomTele&arg1=0&arg2=0&arg3=0&arg4=0' ); - usleep (100000); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&channel=0&code=ZoomTele&arg1=0&arg2=0&arg3=0&arg4=0' ); +sub zoomConTele { + my $self = shift; + Debug('Zoom continuous tele'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=ZoomTele&arg1=0&arg2=0&arg3=0&arg4=0'); + usleep(100000); + $self->sendCmd('cgi-bin/ptz.cgi?action=stop&channel=0&code=ZoomTele&arg1=0&arg2=0&arg3=0&arg4=0'); } -sub zoomConWide -{ - my $self = shift; - Debug( "Zoom continuous wide" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&channel=0&code=ZoomWide&arg1=0&arg2=0&arg3=0&arg4=0' ); - usleep (100000); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&channel=0&code=ZoomWide&arg1=0&arg2=0&arg3=0&arg4=0' ); +sub zoomConWide { + my $self = shift; + Debug('Zoom continuous wide'); + $self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=ZoomWide&arg1=0&arg2=0&arg3=0&arg4=0'); + usleep (100000); + $self->sendCmd('cgi-bin/ptz.cgi?action=stop&channel=0&code=ZoomWide&arg1=0&arg2=0&arg3=0&arg4=0'); } 1; @@ -355,7 +320,7 @@ ZoneMinder::Control::Amcrest_HTTP - Amcrest camera control =head1 DESCRIPTION -This module contains the implementation of the Amcrest Camera +This module contains the implementation of the Amcrest Camera controllable SDK API. NOTE: This module implements interaction with the camera in clear text. diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/SkyIPCam7xx.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/SkyIPCam7xx.pm index 7175d1e84..ebbaf3a8a 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/SkyIPCam7xx.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/SkyIPCam7xx.pm @@ -1,6 +1,6 @@ # ========================================================================== # -# ZoneMinder Airlink SkyIPCam AICN747/AICN747W Control Protocol Module, $Date: 2008-09-13 17:30:29 +0000 (Sat, 13 Sept 2008) $, $Revision: 2229 $ +# ZoneMinder Airlink SkyIPCam AICN747/AICN747W Control Protocol Module # Copyright (C) 2008 Brian Rudy (brudyNO@SPAMpraecogito.com) # # This program is free software; you can redistribute it and/or @@ -43,8 +43,6 @@ our @ISA = qw(ZoneMinder::Control); use ZoneMinder::Logger qw(:all); use ZoneMinder::Config qw(:all); -use Time::HiRes qw( usleep ); - sub open { my $self = shift; @@ -52,58 +50,50 @@ sub open { use LWP::UserAgent; $self->{ua} = LWP::UserAgent->new; - $self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION ); + $self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION); $self->{state} = 'open'; } -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" ); + $self->printMsg($cmd, 'Tx'); my $url; - if ( $self->{Monitor}->{ControlAddress} =~ /^http/ ) { + if ( $self->{Monitor}->{ControlAddress} =~ /^http/i ) { $url = $self->{Monitor}->{ControlAddress}.$cmd; } else { $url = 'http://'.$self->{Monitor}->{ControlAddress}.$cmd; - } # en dif - my $req = HTTP::Request->new( GET=>$url ); + } # end if + my $req = HTTP::Request->new(GET=>$url); my $res = $self->{ua}->request($req); if ( $res->is_success ) { $result = !undef; } else { - Error( "Error check failed: '".$res->status_line()."'" ); + Error('Error check failed: \''.$res->status_line().'\''); } - return( $result ); + return $result; } sub reset { my $self = shift; - Debug( "Camera Reset" ); - my $cmd = "/admin/ptctl.cgi?move=reset"; - $self->sendCmd( $cmd ); + Debug('Camera Reset'); + my $cmd = '/admin/ptctl.cgi?move=reset'; + $self->sendCmd($cmd); } sub moveMap { my $self = shift; my $params = shift; - my $xcoord = $self->getParam( $params, 'xcoord' ); - my $ycoord = $self->getParam( $params, 'ycoord' ); + my $xcoord = $self->getParam($params, 'xcoord'); + my $ycoord = $self->getParam($params, 'ycoord'); my $hor = $xcoord * 100 / $self->{Monitor}->{Width}; my $ver = $ycoord * 100 / $self->{Monitor}->{Height}; @@ -125,81 +115,81 @@ sub moveMap { elsif ( $hor > 50 ) { # right $horSteps = (($hor - 50) / 50) * $maxhor; - $horDir = "right"; + $horDir = 'right'; } # Vertical movement if ( $ver < 50 ) { # up $verSteps = ((50 - $ver) / 50) * $maxver; - $verDir = "up"; + $verDir = 'up'; } elsif ( $ver > 50 ) { # down $verSteps = (($ver - 50) / 50) * $maxver; - $verDir = "down"; + $verDir = 'down'; } my $v = int($verSteps); my $h = int($horSteps); - Debug( "Move Map to $xcoord,$ycoord, hor=$h $horDir, ver=$v $verDir"); + Debug("Move Map to $xcoord,$ycoord, hor=$h $horDir, ver=$v $verDir"); my $cmd = "/cgi/admin/ptctrl.cgi?action=movedegree&Cmd=$horDir&Degree=$h"; - $self->sendCmd( $cmd ); + $self->sendCmd($cmd); $cmd = "/cgi/admin/ptctrl.cgi?action=movedegree&Cmd=$verDir&Degree=$v"; - $self->sendCmd( $cmd ); + $self->sendCmd($cmd); } sub moveRelUp { my $self = shift; my $params = shift; - my $step = $self->getParam( $params, 'tiltstep' ); - Debug( "Step Up $step" ); - my $cmd = "/admin/ptctl.cgi?move=up"; - $self->sendCmd( $cmd ); + my $step = $self->getParam($params, 'tiltstep'); + Debug("Step Up $step"); + my $cmd = '/admin/ptctl.cgi?move=up'; + $self->sendCmd($cmd); } sub moveRelDown { my $self = shift; my $params = shift; - my $step = $self->getParam( $params, 'tiltstep' ); - Debug( "Step Down $step" ); - my $cmd = "/admin/ptctl.cgi?move=down"; - $self->sendCmd( $cmd ); + my $step = $self->getParam($params, 'tiltstep'); + Debug("Step Down $step"); + my $cmd = '/admin/ptctl.cgi?move=down'; + $self->sendCmd($cmd); } sub moveRelLeft { my $self = shift; my $params = shift; - my $step = $self->getParam( $params, 'panstep' ); + my $step = $self->getParam($params, 'panstep'); - if ( $self->{Monitor}->{Orientation} eq "hori" ) { - Debug( "Stepping Right because flipped horizontally " ); - $self->sendCmd( "/admin/ptctl.cgi?move=right" ); + if ( $self->{Monitor}->{Orientation} eq 'FLIP_HORI' ) { + Debug('Stepping Right because flipped horizontally'); + $self->sendCmd('/admin/ptctl.cgi?move=right'); } else { - Debug( "Step Left" ); - $self->sendCmd( "/admin/ptctl.cgi?move=left" ); + Debug('Step Left'); + $self->sendCmd('/admin/ptctl.cgi?move=left'); } } sub moveRelRight { my $self = shift; my $params = shift; - my $step = $self->getParam( $params, 'panstep' ); - if ( $self->{Monitor}->{Orientation} eq "hori" ) { - Debug( "Stepping Left because flipped horizontally " ); - $self->sendCmd( "/admin/ptctl.cgi?move=left" ); + my $step = $self->getParam($params, 'panstep'); + if ( $self->{Monitor}->{Orientation} eq 'FLIP_HORI' ) { + Debug('Stepping Left because flipped horizontally'); + $self->sendCmd('/admin/ptctl.cgi?move=left'); } else { - Debug( "Step Right" ); - $self->sendCmd( "/admin/ptctl.cgi?move=right" ); + Debug('Step Right'); + $self->sendCmd('/admin/ptctl.cgi?move=right'); } } sub presetClear { my $self = shift; my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Clear Preset $preset" ); + my $preset = $self->getParam($params, 'preset'); + Debug("Clear Preset $preset"); #my $cmd = "/axis-cgi/com/ptz.cgi?removeserverpresetno=$preset"; #$self->sendCmd( $cmd ); } @@ -207,26 +197,26 @@ sub presetClear { sub presetSet { my $self = shift; my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Set Preset $preset" ); - my $cmd = "/admin/ptctl.cgi?position=" . ($preset - 1) . "&positionname=zm$preset"; + my $preset = $self->getParam($params, 'preset'); + Debug("Set Preset $preset"); + my $cmd = '/admin/ptctl.cgi?position=' . ($preset - 1) . "&positionname=zm$preset"; $self->sendCmd( $cmd ); } sub presetGoto { my $self = shift; my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Goto Preset $preset" ); - my $cmd = "/admin/ptctl.cgi?move=p" . ($preset - 1); - $self->sendCmd( $cmd ); + my $preset = $self->getParam($params, 'preset'); + Debug("Goto Preset $preset"); + my $cmd = '/admin/ptctl.cgi?move=p'.($preset - 1); + $self->sendCmd($cmd); } sub presetHome { my $self = shift; - Debug( "Home Preset" ); - my $cmd = "/admin/ptctl.cgi?move=h"; - $self->sendCmd( $cmd ); + Debug('Home Preset'); + my $cmd = '/admin/ptctl.cgi?move=h'; + $self->sendCmd($cmd); } 1; diff --git a/src/zm_ffmpeg.cpp b/src/zm_ffmpeg.cpp index 6b6036db5..f0b3dc76e 100644 --- a/src/zm_ffmpeg.cpp +++ b/src/zm_ffmpeg.cpp @@ -81,7 +81,7 @@ void FFMPEGInit() { av_log_set_callback(log_libav_callback); Info("Enabling ffmpeg logs, as LOG_DEBUG+LOG_FFMPEG are enabled in options"); } else { - Info("Not enabling ffmpeg logs, as LOG_FFMPEG and/or LOG_DEBUG is disabled in options, or this monitor not part of your debug targets"); + Info("Not enabling ffmpeg logs, as LOG_FFMPEG and/or LOG_DEBUG is disabled in options, or this monitor is not part of your debug targets"); av_log_set_level(AV_LOG_QUIET); } #if !LIBAVFORMAT_VERSION_CHECK(58, 9, 0, 64, 0) @@ -291,17 +291,18 @@ static void zm_log_fps(double d, const char *postfix) { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) void zm_dump_codecpar ( const AVCodecParameters *par ) { - Debug(1, "Dumping codecpar codec_type(%d) codec_id(%d %s) codec_tag(%d) width(%d) height(%d) bit_rate(%d) format(%d = %s)", - par->codec_type, - par->codec_id, - avcodec_get_name(par->codec_id), - par->codec_tag, - par->width, - par->height, - par->bit_rate, - par->format, - ((AVPixelFormat)par->format == AV_PIX_FMT_NONE ? "none" : av_get_pix_fmt_name((AVPixelFormat)par->format)) -); + Debug(1, "Dumping codecpar codec_type(%d %s) codec_id(%d %s) codec_tag(%" PRIu32 ") width(%d) height(%d) bit_rate(%" PRIu64 ") format(%d %s)", + par->codec_type, + av_get_media_type_string(par->codec_type), + par->codec_id, + avcodec_get_name(par->codec_id), + par->codec_tag, + par->width, + par->height, + par->bit_rate, + par->format, + (((AVPixelFormat)par->format == AV_PIX_FMT_NONE) ? "none" : av_get_pix_fmt_name((AVPixelFormat)par->format)) + ); } #endif diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index c9dec7e05..fafbf4c75 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -1087,6 +1087,7 @@ int FfmpegCamera::transfer_to_image( return -1; } #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + // From what I've read, we should align the linesizes to 32bit so that ffmpeg can use SIMD instructions too. int size = av_image_fill_arrays( output_frame->data, output_frame->linesize, directbuffer, imagePixFormat, width, height, 32); @@ -1128,8 +1129,8 @@ int FfmpegCamera::transfer_to_image( mConvertContext, input_frame->data, input_frame->linesize, 0, mVideoCodecContext->height, output_frame->data, output_frame->linesize); - if ( ret <= 0 ) { - Error("Unable to convert format %u %s linesize %d height %d to format %u %s linesize %d at frame %d codec %u %s : code: %d", + if ( ret < 0 ) { + Error("Unable to convert format %u %s linesize %d height %d to format %u %s linesize %d at frame %d codec %u %s lines %d: code: %d", input_frame->format, av_get_pix_fmt_name((AVPixelFormat)input_frame->format), input_frame->linesize, mVideoCodecContext->height, imagePixFormat, @@ -1137,6 +1138,7 @@ int FfmpegCamera::transfer_to_image( output_frame->linesize, frameCount, mVideoCodecContext->pix_fmt, av_get_pix_fmt_name(mVideoCodecContext->pix_fmt), + mVideoCodecContext->height, ret ); return -1; diff --git a/src/zm_ffmpeg_input.cpp b/src/zm_ffmpeg_input.cpp index ab7cbe8d3..352de347c 100644 --- a/src/zm_ffmpeg_input.cpp +++ b/src/zm_ffmpeg_input.cpp @@ -10,6 +10,7 @@ FFmpeg_Input::FFmpeg_Input() { FFMPEGInit(); streams = NULL; frame = NULL; + last_seek_request = -1; } FFmpeg_Input::~FFmpeg_Input() { @@ -102,7 +103,6 @@ int FFmpeg_Input::Open(const char *filepath) { } // end int FFmpeg_Input::Open( const char * filepath ) AVFrame *FFmpeg_Input::get_frame(int stream_id) { - Debug(1, "Getting frame from stream %d", stream_id); int frameComplete = false; AVPacket packet; @@ -138,12 +138,14 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id) { frame = zm_av_frame_alloc(); } ret = zm_send_packet_receive_frame(context, frame, packet); - if ( ret <= 0 ) { + if ( ret < 0 ) { Error("Unable to decode frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, av_make_error_string(ret).c_str()); zm_av_packet_unref(&packet); av_frame_free(&frame); continue; + } else { + zm_dump_frame(frame, "resulting frame"); } frameComplete = 1; @@ -175,9 +177,20 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id, double at) { } // Have to grab a frame to update our current frame to know where we are get_frame(stream_id); - } // end if ! frame + } // end if ! frame - if ( frame->pts > seek_target ) { + if ( !frame ) { + Warning("Unable to get frame."); + return NULL; + } + + if ( + (last_seek_request >= 0) + && + (last_seek_request > seek_target ) + && + (frame->pts > seek_target) + ) { zm_dump_frame(frame, "frame->pts > seek_target, seek backwards"); // our frame must be beyond our seek target. so go backwards to before it if ( ( ret = av_seek_frame(input_format_context, stream_id, seek_target, @@ -191,6 +204,8 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id, double at) { zm_dump_frame(frame, "frame->pts > seek_target, got"); } // end if frame->pts > seek_target + last_seek_request = seek_target; + // Seeking seems to typically seek to a keyframe, so then we have to decode until we get the frame we want. if ( frame->pts <= seek_target ) { zm_dump_frame(frame, "pts <= seek_target"); diff --git a/src/zm_ffmpeg_input.h b/src/zm_ffmpeg_input.h index 2f524ac45..900f14d4a 100644 --- a/src/zm_ffmpeg_input.h +++ b/src/zm_ffmpeg_input.h @@ -42,6 +42,7 @@ class FFmpeg_Input { int audio_stream_id; AVFormatContext *input_format_context; AVFrame *frame; + int64_t last_seek_request; }; #endif diff --git a/src/zm_monitorstream.cpp b/src/zm_monitorstream.cpp index 0c71b5c61..49d617f7d 100644 --- a/src/zm_monitorstream.cpp +++ b/src/zm_monitorstream.cpp @@ -1,21 +1,21 @@ // // ZoneMinder Monitor Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes -// +// // 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// +// #include "zm.h" #include "zm_db.h" @@ -73,7 +73,7 @@ bool MonitorStream::checkSwapPath(const char *path, bool create_path) { return false; } return true; -} // end bool MonitorStream::checkSwapPath( const char *path, bool create_path ) +} // end bool MonitorStream::checkSwapPath( const char *path, bool create_path ) void MonitorStream::processCommand(const CmdMsg *msg) { Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] ); @@ -265,7 +265,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) { //status_data.enabled = monitor->shared_data->active; status_data.enabled = monitor->trigger_data->trigger_state!=Monitor::TRIGGER_OFF; status_data.forced = monitor->trigger_data->trigger_state==Monitor::TRIGGER_ON; - Debug(2, "Buffer Level:%d, Delayed:%d, Paused:%d, Rate:%d, delay:%.3f, Zoom:%d, Enabled:%d Forced:%d", + Debug(2, "Buffer Level:%d, Delayed:%d, Paused:%d, Rate:%d, delay:%.3f, Zoom:%d, Enabled:%d Forced:%d", status_data.buffer_level, status_data.delayed, status_data.paused, @@ -327,7 +327,7 @@ bool MonitorStream::sendFrame(const char *filepath, struct timeval *timestamp) { // Calculate how long it takes to actually send the frame struct timeval frameStartTime; gettimeofday(&frameStartTime, NULL); - + fputs("--ZoneMinderFrame\r\nContent-Type: image/jpeg\r\n", stdout); fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size); if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) { @@ -383,7 +383,7 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) { // Calculate how long it takes to actually send the frame struct timeval frameStartTime; gettimeofday(&frameStartTime, NULL); - + fputs("--ZoneMinderFrame\r\n", stdout); switch( type ) { case STREAM_JPEG : @@ -412,7 +412,7 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) { } fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size); if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) { - if ( !zm_terminate ) { + if ( !zm_terminate ) { // If the pipe was closed, we will get signalled SIGPIPE to exit, which will set zm_terminate Warning("Unable to send stream frame: %s", strerror(errno)); } @@ -430,7 +430,7 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) { Warning("Frame send time %d msec too slow, throttling maxfps to %.2f", frameSendTime, maxfps); } - } + } // Not mpeg last_frame_sent = TV_2_FLOAT(now); return true; } // end bool MonitorStream::sendFrame( Image *image, struct timeval *timestamp ) @@ -455,7 +455,7 @@ void MonitorStream::runStream() { fputs("Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n", stdout); // point to end which is theoretically not a valid value because all indexes are % image_buffer_count - unsigned int last_read_index = monitor->image_buffer_count; + unsigned int last_read_index = monitor->image_buffer_count; time_t stream_start_time; time(&stream_start_time); @@ -474,22 +474,21 @@ void MonitorStream::runStream() { Image *paused_image = NULL; struct timeval paused_timestamp; - // 15 is the max length for the swap path suffix, /zmswap-whatever, assuming max 6 digits for monitor id - const int max_swap_len_suffix = 15; - - int swap_path_length = staticConfig.PATH_SWAP.length() + 1; // +1 for NULL terminator - int subfolder1_length = snprintf(NULL, 0, "/zmswap-m%d", monitor->Id()) + 1; - int subfolder2_length = snprintf(NULL, 0, "/zmswap-q%06d", connkey) + 1; - int total_swap_path_length = swap_path_length + subfolder1_length + subfolder2_length; - if ( connkey && ( playback_buffer > 0 ) ) { + // 15 is the max length for the swap path suffix, /zmswap-whatever, assuming max 6 digits for monitor id + const int max_swap_len_suffix = 15; + + int swap_path_length = staticConfig.PATH_SWAP.length() + 1; // +1 for NULL terminator + int subfolder1_length = snprintf(NULL, 0, "/zmswap-m%d", monitor->Id()) + 1; + int subfolder2_length = snprintf(NULL, 0, "/zmswap-q%06d", connkey) + 1; + int total_swap_path_length = swap_path_length + subfolder1_length + subfolder2_length; if ( total_swap_path_length + max_swap_len_suffix > PATH_MAX ) { Error("Swap Path is too long. %d > %d ", total_swap_path_length+max_swap_len_suffix, PATH_MAX); } else { swap_path = staticConfig.PATH_SWAP; - Debug( 3, "Checking swap path folder: %s", swap_path.c_str() ); + Debug(3, "Checking swap path folder: %s", swap_path.c_str()); if ( checkSwapPath(swap_path.c_str(), true) ) { swap_path += stringtf("/zmswap-m%d", monitor->Id()); @@ -509,8 +508,8 @@ void MonitorStream::runStream() { } else { Debug(2, "Assigning temporary buffer"); temp_image_buffer = new SwapImage[temp_image_buffer_count]; - memset( temp_image_buffer, 0, sizeof(*temp_image_buffer)*temp_image_buffer_count ); - Debug( 2, "Assigned temporary buffer" ); + memset(temp_image_buffer, 0, sizeof(*temp_image_buffer)*temp_image_buffer_count); + Debug(2, "Assigned temporary buffer"); } } } else { @@ -525,7 +524,7 @@ void MonitorStream::runStream() { Debug(1, "Using %.3f for fps instead of current fps %.3f", capture_max_fps, capture_fps); capture_fps = capture_max_fps; } - + if ( capture_fps < 1 ) { max_secs_since_last_sent_frame = 10/capture_fps; Debug(1, "Adjusting max_secs_since_last_sent_frame to %.2f from current fps %.2f", @@ -562,7 +561,7 @@ void MonitorStream::runStream() { touch(sock_path_lock); last_comm_update = now; } - } // end if connkey + } // end if connkey if ( paused ) { if ( !was_paused ) { @@ -589,7 +588,7 @@ void MonitorStream::runStream() { } else { if ( !paused ) { int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count); - //Debug( 3, "tri: %d, ti: %d", temp_read_index, temp_index ); + // Debug( 3, "tri: %d, ti: %d", temp_read_index, temp_index ); SwapImage *swap_image = &temp_image_buffer[temp_index]; if ( !swap_image->valid ) { @@ -597,51 +596,61 @@ void MonitorStream::runStream() { delayed = true; temp_read_index = MOD_ADD(temp_read_index, (replay_rate>=0?-1:1), temp_image_buffer_count); } else { - //Debug( 3, "siT: %f, lfT: %f", TV_2_FLOAT( swap_image->timestamp ), TV_2_FLOAT( last_frame_timestamp ) ); - double expected_delta_time = ((TV_2_FLOAT( swap_image->timestamp ) - TV_2_FLOAT( last_frame_timestamp )) * ZM_RATE_BASE)/replay_rate; - double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; + // Debug( 3, "siT: %f, lfT: %f", TV_2_FLOAT( swap_image->timestamp ), TV_2_FLOAT( last_frame_timestamp ) ); + double expected_delta_time = ((TV_2_FLOAT(swap_image->timestamp) - TV_2_FLOAT(last_frame_timestamp)) * ZM_RATE_BASE)/replay_rate; + double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent; - //Debug( 3, "eDT: %.3lf, aDT: %.3f, lFS:%.3f, NOW:%.3f", expected_delta_time, actual_delta_time, last_frame_sent, TV_2_FLOAT( now ) ); + // Debug( 3, "eDT: %.3lf, aDT: %.3f, lFS:%.3f, NOW:%.3f", expected_delta_time, actual_delta_time, last_frame_sent, TV_2_FLOAT( now ) ); // If the next frame is due if ( actual_delta_time > expected_delta_time ) { - //Debug( 2, "eDT: %.3lf, aDT: %.3f", expected_delta_time, actual_delta_time ); + // Debug( 2, "eDT: %.3lf, aDT: %.3f", expected_delta_time, actual_delta_time ); if ( temp_index%frame_mod == 0 ) { - Debug( 2, "Sending delayed frame %d", temp_index ); + Debug(2, "Sending delayed frame %d", temp_index); // Send the next frame - if ( ! sendFrame(temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp) ) + if ( ! sendFrame(temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp) ) { zm_terminate = true; + } memcpy(&last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp)); - //frame_sent = true; + // frame_sent = true; } temp_read_index = MOD_ADD(temp_read_index, (replay_rate>0?1:-1), temp_image_buffer_count); } } } else if ( step != 0 ) { - temp_read_index = MOD_ADD( temp_read_index, (step>0?1:-1), temp_image_buffer_count ); + temp_read_index = MOD_ADD(temp_read_index, (step>0?1:-1), temp_image_buffer_count); SwapImage *swap_image = &temp_image_buffer[temp_read_index]; // Send the next frame - if ( !sendFrame( temp_image_buffer[temp_read_index].file_name, &temp_image_buffer[temp_read_index].timestamp ) ) + if ( !sendFrame( + temp_image_buffer[temp_read_index].file_name, + &temp_image_buffer[temp_read_index].timestamp + ) ) { zm_terminate = true; - memcpy( &last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp) ); - //frame_sent = true; + } + memcpy( + &last_frame_timestamp, + &(swap_image->timestamp), + sizeof(last_frame_timestamp) + ); + // frame_sent = true; step = 0; } else { //paused? int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count); - double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; - if ( got_command || actual_delta_time > 5 ) { + double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent; + if ( got_command || (actual_delta_time > 5) ) { // Send keepalive Debug(2, "Sending keepalive frame %d", temp_index); // Send the next frame - if ( !sendFrame( temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp ) ) + if ( !sendFrame(temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp) ) { zm_terminate = true; - //frame_sent = true; + } + // frame_sent = true; } - } // end if (!paused) or step or paused - } // end if have exceeded buffer or not + } // end if (!paused) or step or paused + } // end if have exceeded buffer or not if ( temp_read_index == temp_write_index ) { // Go back to live viewing @@ -652,16 +661,16 @@ void MonitorStream::runStream() { delayed = false; replay_rate = ZM_RATE_BASE; } - } // end if ( buffered_playback && delayed ) + } // end if ( buffered_playback && delayed ) if ( last_read_index != monitor->shared_data->last_write_index ) { // have a new image to send - int index = monitor->shared_data->last_write_index % monitor->image_buffer_count; // % shouldn't be neccessary + int index = monitor->shared_data->last_write_index % monitor->image_buffer_count; // % shouldn't be neccessary if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) { if ( !paused && !delayed ) { last_read_index = monitor->shared_data->last_write_index; Debug(2, "index: %d: frame_mod: %d frame count: %d paused(%d) delayed(%d)", - index, frame_mod, frame_count, paused, delayed ); + index, frame_mod, frame_count, paused, delayed); // Send the next frame Monitor::Snapshot *snap = &monitor->image_buffer[index]; @@ -670,9 +679,13 @@ void MonitorStream::runStream() { Debug(2, "sendFrame failed, quiting."); zm_terminate = true; } - // Perhaps we should use NOW instead. - memcpy(&last_frame_timestamp, snap->timestamp, sizeof(last_frame_timestamp)); - //frame_sent = true; + // Perhaps we should use NOW instead. + memcpy( + &last_frame_timestamp, + snap->timestamp, + sizeof(last_frame_timestamp) + ); + // frame_sent = true; temp_read_index = temp_write_index; } else { @@ -697,7 +710,7 @@ void MonitorStream::runStream() { if ( !sendFrame(paused_image, &paused_timestamp) ) zm_terminate = true; } else { - Debug(2, "Would have sent keepalive frame, but had no paused_image "); + Debug(2, "Would have sent keepalive frame, but had no paused_image"); } } // end if actual_delta_time > 5 } // end if change in zoom @@ -718,27 +731,27 @@ void MonitorStream::runStream() { temp_index); temp_image_buffer[temp_index].valid = true; } - memcpy( &(temp_image_buffer[temp_index].timestamp), monitor->image_buffer[index].timestamp, sizeof(temp_image_buffer[0].timestamp) ); - monitor->image_buffer[index].image->WriteJpeg( temp_image_buffer[temp_index].file_name, config.jpeg_file_quality ); - temp_write_index = MOD_ADD( temp_write_index, 1, temp_image_buffer_count ); + memcpy(&(temp_image_buffer[temp_index].timestamp), monitor->image_buffer[index].timestamp, sizeof(temp_image_buffer[0].timestamp)); + monitor->image_buffer[index].image->WriteJpeg(temp_image_buffer[temp_index].file_name, config.jpeg_file_quality); + temp_write_index = MOD_ADD(temp_write_index, 1, temp_image_buffer_count); if ( temp_write_index == temp_read_index ) { // Go back to live viewing - Warning( "Exceeded temporary buffer, resuming live play" ); + Warning("Exceeded temporary buffer, resuming live play"); paused = false; delayed = false; replay_rate = ZM_RATE_BASE; } } else { - Warning( "Unable to store frame as timestamp invalid" ); + Warning("Unable to store frame as timestamp invalid"); } } else { - Warning( "Unable to store frame as shared memory invalid" ); + Warning("Unable to store frame as shared memory invalid"); } } // end if buffered playback frame_count++; } else { Debug(3, "Waiting for capture last_write_index=%u", monitor->shared_data->last_write_index); - } // end if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index ) + } // end if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index ) unsigned long sleep_time = (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))); Debug(3, "Sleeping for (%d)", sleep_time); @@ -755,10 +768,10 @@ void MonitorStream::runStream() { break; } } - if ( ! last_frame_sent ) { + if ( !last_frame_sent ) { // If we didn't capture above, because frame_mod was bad? Then last_frame_sent will not have a value. last_frame_sent = now.tv_sec; - Warning("no last_frame_sent. Shouldn't happen. frame_mod was (%d) frame_count (%d) ", + Warning("no last_frame_sent. Shouldn't happen. frame_mod was (%d) frame_count (%d)", frame_mod, frame_count); } else if ( (!paused) @@ -799,9 +812,9 @@ void MonitorStream::runStream() { } } } - globfree( &pglob ); + globfree(&pglob); if ( rmdir(swap_path.c_str()) < 0 ) { - Error( "Can't rmdir '%s': %s", swap_path.c_str(), strerror(errno) ); + Error("Can't rmdir '%s': %s", swap_path.c_str(), strerror(errno)); } } // end if checking for swap_path } // end if buffered_playback @@ -809,7 +822,7 @@ void MonitorStream::runStream() { closeComms(); } // end MonitorStream::runStream -void MonitorStream::SingleImage( int scale ) { +void MonitorStream::SingleImage(int scale) { int img_buffer_size = 0; static JOCTET img_buffer[ZM_MAX_IMAGE_SIZE]; Image scaled_image; @@ -817,42 +830,45 @@ void MonitorStream::SingleImage( int scale ) { Image *snap_image = snap->image; if ( scale != ZM_SCALE_BASE ) { - scaled_image.Assign( *snap_image ); - scaled_image.Scale( scale ); + scaled_image.Assign(*snap_image); + scaled_image.Scale(scale); snap_image = &scaled_image; } if ( !config.timestamp_on_capture ) { - monitor->TimestampImage( snap_image, snap->timestamp ); + monitor->TimestampImage(snap_image, snap->timestamp); } - snap_image->EncodeJpeg( img_buffer, &img_buffer_size ); - - fprintf( stdout, "Content-Length: %d\r\n", img_buffer_size ); - fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" ); - fwrite( img_buffer, img_buffer_size, 1, stdout ); + snap_image->EncodeJpeg(img_buffer, &img_buffer_size); + + fprintf(stdout, + "Content-Length: %d\r\n" + "Content-Type: image/jpeg\r\n\r\n", + img_buffer_size); + fwrite(img_buffer, img_buffer_size, 1, stdout); } -void MonitorStream::SingleImageRaw( int scale ) { +void MonitorStream::SingleImageRaw(int scale) { Image scaled_image; Monitor::Snapshot *snap = monitor->getSnapshot(); Image *snap_image = snap->image; if ( scale != ZM_SCALE_BASE ) { - scaled_image.Assign( *snap_image ); - scaled_image.Scale( scale ); + scaled_image.Assign(*snap_image); + scaled_image.Scale(scale); snap_image = &scaled_image; } if ( !config.timestamp_on_capture ) { - monitor->TimestampImage( snap_image, snap->timestamp ); + monitor->TimestampImage(snap_image, snap->timestamp); } - - fprintf( stdout, "Content-Length: %d\r\n", snap_image->Size() ); - fprintf( stdout, "Content-Type: image/x-rgb\r\n\r\n" ); - fwrite( snap_image->Buffer(), snap_image->Size(), 1, stdout ); + + fprintf(stdout, + "Content-Length: %d\r\n" + "Content-Type: image/x-rgb\r\n\r\n", + snap_image->Size()); + fwrite(snap_image->Buffer(), snap_image->Size(), 1, stdout); } - #ifdef HAVE_ZLIB_H -void MonitorStream::SingleImageZip( int scale ) { +void MonitorStream::SingleImageZip(int scale) { unsigned long img_buffer_size = 0; static Bytef img_buffer[ZM_MAX_IMAGE_SIZE]; Image scaled_image; @@ -861,17 +877,19 @@ void MonitorStream::SingleImageZip( int scale ) { Image *snap_image = snap->image; if ( scale != ZM_SCALE_BASE ) { - scaled_image.Assign( *snap_image ); - scaled_image.Scale( scale ); + scaled_image.Assign(*snap_image); + scaled_image.Scale(scale); snap_image = &scaled_image; } if ( !config.timestamp_on_capture ) { - monitor->TimestampImage( snap_image, snap->timestamp ); + monitor->TimestampImage(snap_image, snap->timestamp); } - snap_image->Zip( img_buffer, &img_buffer_size ); - - fprintf( stdout, "Content-Length: %ld\r\n", img_buffer_size ); - fprintf( stdout, "Content-Type: image/x-rgbz\r\n\r\n" ); - fwrite( img_buffer, img_buffer_size, 1, stdout ); + snap_image->Zip(img_buffer, &img_buffer_size); + + fprintf(stdout, + "Content-Length: %ld\r\n" + "Content-Type: image/x-rgbz\r\n\r\n", + img_buffer_size); + fwrite(img_buffer, img_buffer_size, 1, stdout); } #endif // HAVE_ZLIB_H diff --git a/utils/do_debian_package.sh b/utils/do_debian_package.sh index 3e20b303a..b60996a78 100755 --- a/utils/do_debian_package.sh +++ b/utils/do_debian_package.sh @@ -175,7 +175,7 @@ cd ../ VERSION=`cat ${GITHUB_FORK}_zoneminder_release/version` -if [ $VERSION == "" ]; then +if [ -z "$VERSION" ]; then exit 1; fi; if [ "$SNAPSHOT" != "stable" ] && [ "$SNAPSHOT" != "" ]; then diff --git a/utils/packpack/rsync_xfer.sh b/utils/packpack/rsync_xfer.sh index 40f5235be..c4b6f93e1 100755 --- a/utils/packpack/rsync_xfer.sh +++ b/utils/packpack/rsync_xfer.sh @@ -1,5 +1,10 @@ #!/bin/bash +# We don't deploy during eslint checks, so exit immediately +if [ "${DIST}" == "eslint" ]; then + exit 0 +fi + # Check to see if this script has access to all the commands it needs for CMD in sshfs rsync find fusermount mkdir; do type $CMD 2>&1 > /dev/null @@ -12,53 +17,26 @@ for CMD in sshfs rsync find fusermount mkdir; do fi done -# We only want to deploy packages during cron events -# See https://docs.travis-ci.com/user/cron-jobs/ -if [ "${TRAVIS_EVENT_TYPE}" == "cron" ] || [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ]; then - - if [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ]; then - targetfolder="debian/master/mini-dinstall/incoming" - else - targetfolder="travis" - fi - - echo - echo "Target subfolder set to $targetfolder" - echo - if [ "${USE_SFTP}" == "yes" ]; then - echo "Running \$(rsync -v -e 'ssh -vvv' build/* zmrepo@zmrepo.zoneminder.com:${targetfolder}/ 2>&1)" - rsync -v -e 'ssh -vvv' build/* zmrepo@zmrepo.zoneminder.com:${targetfolder}/ 2>&1 - if [ $? -eq 0 ]; then - echo - echo "Files copied successfully." - echo - else - echo - echo "ERROR: Attempt to rsync to zmrepo.zoneminder.com failed!" - echo - exit 99 - fi - else - mkdir -p ./zmrepo - ssh_mntchk="$(sshfs zmrepo@zmrepo.zoneminder.com:./ ./zmrepo -o workaround=rename,reconnect 2>&1)" - - if [ -z "$ssh_mntchk" ]; then - echo - echo "Remote filesystem mounted successfully." - echo "Begin transfering files..." - echo - - # Don't keep packages older than 5 days - find ./zmrepo/$targetfolder/ -maxdepth 1 -type f,l -mtime +5 -delete - rsync -vzlh --ignore-errors build/* zmrepo/$targetfolder/ - fusermount -zu zmrepo - else - echo - echo "ERROR: Attempt to mount zmrepo.zoneminder.com failed!" - echo "sshfs gave the following error message:" - echo \"$ssh_mntchk\" - echo - exit 99 - fi - fi +if [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbian" ]; then + targetfolder="debian/master/mini-dinstall/incoming" +else + targetfolder="travis" +fi + +echo +echo "Target subfolder set to $targetfolder" +echo + +echo "Running \$(rsync -v -e 'ssh -vvv' build/*.{rpm,deb,dsc,tar.xz,changes} zmrepo@zmrepo.zoneminder.com:${targetfolder}/ 2>&1)" +rsync -v --ignore-missing-args --exclude 'external-repo.noarch.rpm' -e 'ssh -vvv' build/*.{rpm,deb,dsc,tar.xz,changes} zmrepo@zmrepo.zoneminder.com:${targetfolder}/ 2>&1 +if [ "$?" -eq 0 ]; then + echo + echo "Files copied successfully." + echo +else + echo + echo "ERROR: Attempt to rsync to zmrepo.zoneminder.com failed!" + echo "See log output for details." + echo + exit 99 fi diff --git a/utils/packpack/startpackpack.sh b/utils/packpack/startpackpack.sh index 35a6aeab4..6a51e2995 100755 --- a/utils/packpack/startpackpack.sh +++ b/utils/packpack/startpackpack.sh @@ -9,7 +9,7 @@ # General sanity checks checksanity () { # Check to see if this script has access to all the commands it needs - for CMD in set echo curl git ln mkdir rmdir cat patch; do + for CMD in set echo curl git ln mkdir rmdir cat patch sed; do type $CMD 2>&1 > /dev/null if [ $? -ne 0 ]; then @@ -30,7 +30,7 @@ checksanity () { ARCH="x86_64" fi - if [[ "${ARCH}" != "x86_64" && "${ARCH}" != "i386" && "${ARCH}" != "armhf" ]]; then + if [[ "${ARCH}" != "x86_64" && "${ARCH}" != "i386" && "${ARCH}" != "armhf" && "${ARCH}" != "aarch64" ]]; then echo echo "ERROR: Unsupported architecture specified \"${ARCH}\"." echo @@ -150,7 +150,7 @@ install_deb () { exit 1 fi - # Install and test the zoneminder package (only) for Ubuntu Trusty + # Install and test the zoneminder package (only) for Ubuntu Xenial pkgname="build/zoneminder_${VERSION}-${RELEASE}_amd64.deb" if [ -e $pkgname ]; then @@ -275,6 +275,8 @@ checkdeploytarget () { echo "*** TRACEROUTE ***" echo traceroute -w 2 -m 15 ${DEPLOYTARGET} + + exit 97 fi } @@ -291,43 +293,43 @@ if [ "${TRAVIS}" == "true" ]; then fi checksanity -# We don't want to build packages for all supported distros after every commit -# Only build all packages when executed via cron -# See https://docs.travis-ci.com/user/cron-jobs/ - # Steps common to Redhat distros if [ "${OS}" == "el" ] || [ "${OS}" == "fedora" ]; then - if [ "${TRAVIS_EVENT_TYPE}" == "cron" ] || [ "${TRAVIS}" != "true" ]; then - commonprep - echo "Begin Redhat build..." + commonprep + echo "Begin Redhat build..." - setrpmpkgname + # Newer Redhat distros use dnf package manager rather than yum + if [ "${DIST}" -gt "7" ]; then + sed -i 's\yum\dnf\' utils/packpack/redhat_package.mk + fi - ln -sfT distros/redhat rpm + setrpmpkgname - # The rpm specfile requires the Crud submodule folder to be empty - rm -rf web/api/app/Plugin/Crud - mkdir web/api/app/Plugin/Crud + ln -sfT distros/redhat rpm - reporpm="rpmfusion-free-release" - dlurl="https://download1.rpmfusion.org/free/${OS}/${reporpm}-${DIST}.noarch.rpm" + # The rpm specfile requires the Crud submodule folder to be empty + rm -rf web/api/app/Plugin/Crud + mkdir web/api/app/Plugin/Crud - # Give our downloaded repo rpm a common name so redhat_package.mk can find it - if [ -n "$dlurl" ] && [ $? -eq 0 ]; then - echo "Retrieving ${reporpm} repo rpm..." - curl $dlurl > build/external-repo.noarch.rpm - else - echo "ERROR: Failed to retrieve ${reporpm} repo rpm..." - echo "Download url was: $dlurl" - exit 1 - fi + reporpm="rpmfusion-free-release" + dlurl="https://download1.rpmfusion.org/free/${OS}/${reporpm}-${DIST}.noarch.rpm" - setrpmchangelog + # Give our downloaded repo rpm a common name so redhat_package.mk can find it + if [ -n "$dlurl" ] && [ $? -eq 0 ]; then + echo "Retrieving ${reporpm} repo rpm..." + curl $dlurl > build/external-repo.noarch.rpm + else + echo "ERROR: Failed to retrieve ${reporpm} repo rpm..." + echo "Download url was: $dlurl" + exit 1 + fi - echo "Starting packpack..." - execpackpack - fi; - # Steps common to Debian based distros + setrpmchangelog + + echo "Starting packpack..." + execpackpack + +# Steps common to Debian based distros elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbian" ]; then commonprep echo "Begin ${OS} ${DIST} build..." @@ -348,14 +350,27 @@ elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbia echo "Starting packpack..." execpackpack - # We were not triggered via cron so just build and test trusty - if [ "${OS}" == "ubuntu" ] && [ "${DIST}" == "xenial" ] && [ "${ARCH}" == "x86_64" ]; then - # If we are running inside Travis then attempt to install the deb we just built - if [ "${TRAVIS}" == "true" ]; then + # Try to install and run the newly built zoneminder package + if [ "${OS}" == "ubuntu" ] && [ "${DIST}" == "xenial" ] && [ "${ARCH}" == "x86_64" ] && [ "${TRAVIS}" == "true" ]; then + echo "Begin Deb package installation..." install_deb - fi fi + +# Steps common to eslint checks +elif [ "${OS}" == "eslint" ] || [ "${DIST}" == "eslint" ]; then + + # Check we've got npm installed + type npm 2>&1 > /dev/null + + if [ $? -ne 0 ]; then + echo + echo "ERROR: The script cannot find the required command \"npm\"." + echo + exit 1 + fi + + npm install -g eslint@5.12.0 eslint-config-google@0.11.0 eslint-plugin-html@5.0.0 eslint-plugin-php-markup@0.2.5 + echo "Begin eslint checks..." + eslint --ext .php,.js . fi -exit 0 - diff --git a/version b/version index 63984dc0b..c0bd57583 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.33.15 +1.33.16 diff --git a/web/ajax/log.php b/web/ajax/log.php index ef2e450f2..36a29adc3 100644 --- a/web/ajax/log.php +++ b/web/ajax/log.php @@ -41,7 +41,7 @@ function buildLogQuery($action) { } foreach ( $filter as $field=>$value ) { - if ( ! in_array($field, $filterFields) ) { + if ( !in_array($field, $filterFields) ) { ZM\Error("'$field' is not in valid filter fields " . print_r($filterField,true)); continue; } @@ -58,7 +58,7 @@ function buildLogQuery($action) { $sql .= ' ORDER BY '.$sortField.' '.$sortOrder.' LIMIT '.$limit; return array('sql'=>$sql, 'values'=>$values); -} +} # function buildLogQuery($action) switch ( $_REQUEST['task'] ) { case 'create' : @@ -70,14 +70,16 @@ switch ( $_REQUEST['task'] ) { $string = $_POST['message']; $file = !empty($_POST['file']) ? preg_replace( '/\w+:\/\/[\w.:]+\//', '', $_POST['file'] ) : ''; - if ( !empty( $_POST['line'] ) ) + if ( !empty( $_POST['line'] ) ) { $line = validInt($_POST['line']); - else + } else { $line = NULL; + } $levels = array_flip(ZM\Logger::$codes); - if ( !isset($levels[$_POST['level']]) ) + if ( !isset($levels[$_POST['level']]) ) { ZM\Panic('Unexpected logger level '.$_POST['level']); + } $level = $levels[$_POST['level']]; ZM\Logger::fetch()->logPrint($level, $string, $file, $line); } @@ -141,6 +143,10 @@ switch ( $_REQUEST['task'] ) { $logs[] = $log; } + foreach ( $options as $field => $values ) { + asort($options[$field]); + } + $available = count($logs); ajaxResponse( array( 'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG)?strftime(DATE_FMT_CONSOLE_LONG):date(DATE_FMT_CONSOLE_LONG), diff --git a/web/api/app/Controller/GroupsController.php b/web/api/app/Controller/GroupsController.php index 6f0a88300..5d19b5d98 100644 --- a/web/api/app/Controller/GroupsController.php +++ b/web/api/app/Controller/GroupsController.php @@ -77,16 +77,24 @@ class GroupsController extends AppController { } $this->Group->create(); - if ( $this->Group->save($this->request->data) ) { + + if ( $this->request->data['Group']['MonitorIds'] and ! isset($this->request->data['Monitor']) ) { + $this->request->data['Monitor'] = explode(',', $this->request->data['Group']['MonitorIds']); + unset($this->request->data['Group']['MonitorIds']); + } + if ( $this->Group->saveAssociated($this->request->data, array('atomic'=>true)) ) { return $this->flash( __('The group has been saved.'), array('action' => 'index') ); - } - } - $monitors = $this->Group->Monitor->find('list'); + } else { + ZM\Error("Failed to save Group"); + debug($this->Group->invalidFields()); + } + } # end if post + $monitors = $this->Group->Monitor->find('list'); $this->set(compact('monitors')); - } + } # end add /** * edit method diff --git a/web/api/app/Controller/HostController.php b/web/api/app/Controller/HostController.php index 300352949..7ddba57df 100644 --- a/web/api/app/Controller/HostController.php +++ b/web/api/app/Controller/HostController.php @@ -136,7 +136,7 @@ class HostController extends AppController { }*/ $access_issued_at = time(); - $access_ttl = (ZM_AUTH_HASH_TTL || 2) * 3600; + $access_ttl = max(ZM_AUTH_HASH_TTL,1) * 3600; // by default access token will expire in 2 hrs // you can change it by changing the value of ZM_AUTH_HASH_TLL diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index 3be9166b8..789307795 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -44,33 +44,25 @@ class MonitorsController extends AppController { } else { $conditions = array(); } - global $user; $allowedMonitors = $user ? preg_split('@,@', $user['MonitorIds'], NULL, PREG_SPLIT_NO_EMPTY) : null; if ( $allowedMonitors ) { $conditions['Monitor.Id' ] = $allowedMonitors; } - $find_array = array('conditions'=>$conditions,'contain'=>array('Group')); - if ( isset($conditions['GroupId']) ) { - $find_array['joins'] = array( + $find_array = array( + 'conditions' => &$conditions, + 'contain' => array('Group'), + 'joins' => array( array( 'table' => 'Groups_Monitors', - 'type' => 'inner', + 'type' => 'left', 'conditions' => array( - 'Groups_Monitors.MonitorId = Monitor.Id' + 'Groups_Monitors.MonitorId = Monitor.Id', ), ), - //array( - //'table' => 'Groups', - //'type' => 'inner', - //'conditions' => array( - //'Groups.Id = Groups_Monitors.GroupId', - //'Groups.Id' => $this->request->params['GroupId'], - //), - //) - ); - } + ) + ); $monitors = $this->Monitor->find('all',$find_array); $this->set(array( 'monitors' => $monitors, diff --git a/web/api/app/Model/Group.php b/web/api/app/Model/Group.php index 108f9b9c7..04d783b14 100644 --- a/web/api/app/Model/Group.php +++ b/web/api/app/Model/Group.php @@ -59,7 +59,7 @@ class Group extends AppModel { * * @var array */ - public $hasMany = array( + public $hasAndBelongsToMany = array( 'Monitor' => array( 'className' => 'Monitor', 'joinTable' => 'Groups_Monitors', @@ -77,4 +77,5 @@ class Group extends AppModel { 'counterQuery' => '' ), ); + var $actsAs = array( 'Containable' ); } diff --git a/web/includes/Control.php b/web/includes/Control.php index 39c259092..2121061b1 100644 --- a/web/includes/Control.php +++ b/web/includes/Control.php @@ -128,12 +128,16 @@ class Control extends ZM_Object { $cmds['PresetHome'] = 'presetHome'; if ( $this->CanZoom() ) { - if ( $this->CanZoomCon() ) + if ( $this->CanZoomCon() ) { $cmds['ZoomRoot'] = 'zoomCon'; - elseif ( $this->CanZoomRel() ) + } else if ( $this->CanZoomRel() ) { $cmds['ZoomRoot'] = 'zoomRel'; - elseif ( $this->CanZoomAbs() ) + } else if ( $this->CanZoomAbs() ) { $cmds['ZoomRoot'] = 'zoomAbs'; + } else { + $cmds['ZoomRoot'] = ''; + Error('No zoom type selected. Please select Continuous, Relative, Absolute'); + } $cmds['ZoomTele'] = $cmds['ZoomRoot'].'Tele'; $cmds['ZoomWide'] = $cmds['ZoomRoot'].'Wide'; $cmds['ZoomStop'] = 'zoomStop'; @@ -142,12 +146,16 @@ class Control extends ZM_Object { } if ( $this->CanFocus() ) { - if ( $this->CanFocusCon() ) + if ( $this->CanFocusCon() ) { $cmds['FocusRoot'] = 'focusCon'; - elseif ( $this->CanFocusRel() ) + } else if ( $this->CanFocusRel() ) { $cmds['FocusRoot'] = 'focusRel'; - elseif ( $this->CanFocusAbs() ) + } else if ( $this->CanFocusAbs() ) { $cmds['FocusRoot'] = 'focusAbs'; + } else { + $cmds['FocusRoot'] = ''; + Error('No focus type selected. Please select Continuous, Relative, Absolute'); + } $cmds['FocusFar'] = $cmds['FocusRoot'].'Far'; $cmds['FocusNear'] = $cmds['FocusRoot'].'Near'; $cmds['FocusStop'] = 'focusStop'; @@ -156,12 +164,16 @@ class Control extends ZM_Object { } if ( $this->CanIris() ) { - if ( $this->CanIrisCon() ) + if ( $this->CanIrisCon() ) { $cmds['IrisRoot'] = 'irisCon'; - elseif ( $this->CanIrisRel() ) + } else if ( $this->CanIrisRel() ) { $cmds['IrisRoot'] = 'irisRel'; - elseif ( $this->CanIrisAbs() ) + } else if ( $this->CanIrisAbs() ) { $cmds['IrisRoot'] = 'irisAbs'; + } else { + $cmds['IrisRoot'] = ''; + Error('No iris type selected. Please select Continuous, Relative, Absolute'); + } $cmds['IrisOpen'] = $cmds['IrisRoot'].'Open'; $cmds['IrisClose'] = $cmds['IrisRoot'].'Close'; $cmds['IrisStop'] = 'irisStop'; @@ -170,12 +182,16 @@ class Control extends ZM_Object { } if ( $this->CanWhite() ) { - if ( $this->CanWhiteCon() ) + if ( $this->CanWhiteCon() ) { $cmds['WhiteRoot'] = 'whiteCon'; - elseif ( $this->CanWhiteRel() ) + } else if ( $this->CanWhiteRel() ) { $cmds['WhiteRoot'] = 'whiteRel'; - elseif ( $this->CanWhiteAbs() ) + } else if ( $this->CanWhiteAbs() ) { $cmds['WhiteRoot'] = 'whiteAbs'; + } else { + Error('No White type selected. Please select Continuous, Relative, Absolute'); + $cmds['WhiteRoot'] = ''; + } $cmds['WhiteIn'] = $cmds['WhiteRoot'].'In'; $cmds['WhiteOut'] = $cmds['WhiteRoot'].'Out'; $cmds['WhiteAuto'] = 'whiteAuto'; @@ -183,12 +199,16 @@ class Control extends ZM_Object { } if ( $this->CanGain() ) { - if ( $this->CanGainCon() ) + if ( $this->CanGainCon() ) { $cmds['GainRoot'] = 'gainCon'; - elseif ( $this->CanGainRel() ) + } else if ( $this->CanGainRel() ) { $cmds['GainRoot'] = 'gainRel'; - elseif ( $this->CanGainAbs() ) + } else if ( $this->CanGainAbs() ) { $cmds['GainRoot'] = 'gainAbs'; + } else { + Error('No Gain type selected'); + $cmds['GainRoot'] = ''; + } $cmds['GainUp'] = $cmds['GainRoot'].'Up'; $cmds['GainDown'] = $cmds['GainRoot'].'Down'; $cmds['GainAuto'] = 'gainAuto'; @@ -207,6 +227,7 @@ class Control extends ZM_Object { $cmds['Center'] = $cmds['PresetHome']; } else { $cmds['MoveRoot'] = ''; + Error('No move type selected. Please select Continuous, Relative, Absolute'); } $cmds['MoveUp'] = $cmds['MoveRoot'].'Up'; diff --git a/web/includes/Event.php b/web/includes/Event.php index 460d6eaa1..da633f439 100644 --- a/web/includes/Event.php +++ b/web/includes/Event.php @@ -51,10 +51,10 @@ class Event extends ZM_Object { if ( $new ) { $this->{'Storage'} = $new; } - if ( ! ( array_key_exists('Storage', $this) and $this->{'Storage'} ) ) { + if ( ! ( property_exists($this, 'Storage') and $this->{'Storage'} ) ) { if ( isset($this->{'StorageId'}) and $this->{'StorageId'} ) $this->{'Storage'} = Storage::find_one(array('Id'=>$this->{'StorageId'})); - if ( ! ( array_key_exists('Storage', $this) and $this->{'Storage'} ) ) + if ( ! ( property_exists($this, 'Storage') and $this->{'Storage'} ) ) $this->{'Storage'} = new Storage(NULL); } return $this->{'Storage'}; @@ -64,10 +64,10 @@ class Event extends ZM_Object { if ( $new ) { $this->{'SecondaryStorage'} = $new; } - if ( ! ( array_key_exists('SecondaryStorage', $this) and $this->{'SecondaryStorage'} ) ) { + if ( ! ( property_exists($this, 'SecondaryStorage') and $this->{'SecondaryStorage'} ) ) { if ( isset($this->{'SecondaryStorageId'}) and $this->{'SecondaryStorageId'} ) $this->{'SecondaryStorage'} = Storage::find_one(array('Id'=>$this->{'SecondaryStorageId'})); - if ( ! ( array_key_exists('SecondaryStorage', $this) and $this->{'SecondaryStorage'} ) ) + if ( ! ( property_exists($this, 'SecondaryStorage') and $this->{'SecondaryStorage'} ) ) $this->{'SecondaryStorage'} = new Storage(NULL); } return $this->{'SecondaryStorage'}; @@ -262,7 +262,7 @@ class Event extends ZM_Object { if ( is_null($new) or ( $new != '' ) ) { $this->{'DiskSpace'} = $new; } - if ( (!array_key_exists('DiskSpace',$this)) or (null === $this->{'DiskSpace'}) ) { + if ( (!property_exists($this, 'DiskSpace')) or (null === $this->{'DiskSpace'}) ) { $this->{'DiskSpace'} = folder_size($this->Path()); dbQuery('UPDATE Events SET DiskSpace=? WHERE Id=?', array($this->{'DiskSpace'}, $this->{'Id'})); } @@ -298,7 +298,7 @@ class Event extends ZM_Object { } // end function createListThumbnail function ThumbnailWidth( ) { - if ( ! ( array_key_exists('ThumbnailWidth', $this) ) ) { + if ( ! ( property_exists($this, 'ThumbnailWidth') ) ) { if ( ZM_WEB_LIST_THUMB_WIDTH ) { $this->{'ThumbnailWidth'} = ZM_WEB_LIST_THUMB_WIDTH; $scale = (SCALE_BASE*ZM_WEB_LIST_THUMB_WIDTH)/$this->{'Width'}; @@ -315,7 +315,7 @@ class Event extends ZM_Object { } // end function ThumbnailWidth function ThumbnailHeight( ) { - if ( ! ( array_key_exists('ThumbnailHeight', $this) ) ) { + if ( ! ( property_exists($this, 'ThumbnailHeight') ) ) { if ( ZM_WEB_LIST_THUMB_WIDTH ) { $this->{'ThumbnailWidth'} = ZM_WEB_LIST_THUMB_WIDTH; $scale = (SCALE_BASE*ZM_WEB_LIST_THUMB_WIDTH)/$this->{'Width'}; diff --git a/web/includes/Filter.php b/web/includes/Filter.php index fa7c41bff..2ecf7b61e 100644 --- a/web/includes/Filter.php +++ b/web/includes/Filter.php @@ -39,8 +39,8 @@ class Filter extends ZM_Object { $this->{'Query'} = func_get_arg(0);; $this->{'Query_json'} = jsonEncode($this->{'Query'}); } - if ( !array_key_exists('Query', $this) ) { - if ( array_key_exists('Query_json', $this) and $this->{'Query_json'} ) { + if ( !property_exists($this, 'Query') ) { + if ( property_exists($this, 'Query_json') and $this->{'Query_json'} ) { $this->{'Query'} = jsonDecode($this->{'Query_json'}); } else { $this->{'Query'} = array(); diff --git a/web/includes/Group.php b/web/includes/Group.php index b329946a3..3a34e0af4 100644 --- a/web/includes/Group.php +++ b/web/includes/Group.php @@ -18,7 +18,7 @@ class Group extends ZM_Object { } public function delete() { - if ( array_key_exists('Id', $this) ) { + if ( property_exists($this, 'Id') ) { dbQuery('DELETE FROM Groups_Monitors WHERE GroupId=?', array($this->{'Id'})); dbQuery('UPDATE Groups SET ParentId=NULL WHERE ParentId=?', array($this->{'Id'})); dbQuery('DELETE FROM Groups WHERE Id=?', array($this->{'Id'})); @@ -35,7 +35,7 @@ class Group extends ZM_Object { if ( isset($new) ) { $this->{'depth'} = $new; } - if ( !array_key_exists('depth', $this) or ($this->{'depth'} === null) ) { + if ( !property_exists($this, 'depth') or ($this->{'depth'} === null) ) { $this->{'depth'} = 0; if ( $this->{'ParentId'} != null ) { $Parent = Group::find_one(array('Id'=>$this->{'ParentId'})); @@ -46,7 +46,7 @@ class Group extends ZM_Object { } // end public function depth public function MonitorIds( ) { - if ( ! array_key_exists('MonitorIds', $this) ) { + if ( ! property_exists($this, 'MonitorIds') ) { $this->{'MonitorIds'} = dbFetchAll('SELECT MonitorId FROM Groups_Monitors WHERE GroupId=?', 'MonitorId', array($this->{'Id'})); } return $this->{'MonitorIds'}; diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index a9be54405..4575d2ab6 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -9,118 +9,119 @@ require_once('Storage.php'); class Monitor extends ZM_Object { protected static $table = 'Monitors'; -protected $defaults = array( - 'Id' => null, - 'Name' => '', - 'ServerId' => 0, - 'StorageId' => 0, - 'Type' => 'Ffmpeg', - 'Function' => 'Mocord', - 'Enabled' => array('type'=>'boolean','default'=>1), - 'LinkedMonitors' => array('type'=>'set', 'default'=>null), - 'Triggers' => array('type'=>'set','default'=>''), - 'Device' => '', - 'Channel' => 0, - 'Format' => '0', - 'V4LMultiBuffer' => null, - 'V4LCapturesPerFrame' => 1, - 'Protocol' => null, - 'Method' => '', - 'Host' => null, - 'Port' => '', - 'SubPath' => '', - 'Path' => null, - 'Options' => null, - 'User' => null, - 'Pass' => null, - // These are NOT NULL default 0 in the db, but 0 is not a valid value. FIXME - 'Width' => null, - 'Height' => null, - 'Colours' => 4, - 'Palette' => '0', - 'Orientation' => null, - 'Deinterlacing' => 0, - 'DecoderHWAccelName' => null, - 'DecoderHWAccelDevice' => null, - 'SaveJPEGs' => 3, - 'VideoWriter' => '0', - 'OutputCodec' => null, - 'OutputContainer' => null, - 'EncoderParameters' => "# Lines beginning with # are a comment \n# For changing quality, use the crf option\n# 1 is best, 51 is worst quality\n#crf=23\n", - 'RecordAudio' => array('type'=>'boolean', 'default'=>0), - 'RTSPDescribe' => array('type'=>'boolean','default'=>0), - 'Brightness' => -1, - 'Contrast' => -1, - 'Hue' => -1, - 'Colour' => -1, - 'EventPrefix' => 'Event-', - 'LabelFormat' => '%N - %d/%m/%y %H:%M:%S', - 'LabelX' => 0, - 'LabelY' => 0, - 'LabelSize' => 1, - 'ImageBufferCount' => 100, - 'WarmupCount' => 0, - 'PreEventCount' => 0, - 'PostEventCount' => 0, - 'StreamReplayBuffer' => 0, - 'AlarmFrameCount' => 1, - 'SectionLength' => 600, - 'MinSectionLength' => 10, - 'FrameSkip' => 0, - 'MotionFrameSkip' => 0, - 'AnalysisFPSLimit' => null, - 'AnalysisUpdateDelay' => 0, - 'MaxFPS' => null, - 'AlarmMaxFPS' => null, - 'FPSReportInterval' => 100, - 'RefBlendPerc' => 6, - 'AlarmRefBlendPerc' => 6, - 'Controllable' => array('type'=>'boolean','default'=>0), - 'ControlId' => null, - 'ControlDevice' => null, - 'ControlAddress' => null, - 'AutoStopTimeout' => null, - 'TrackMotion' => array('type'=>'boolean','default'=>0), - 'TrackDelay' => null, - 'ReturnLocation' => -1, - 'ReturnDelay' => null, - 'DefaultRate' => 100, - 'DefaultScale' => 100, - 'SignalCheckPoints' => 0, - 'SignalCheckColour' => '#0000BE', - 'WebColour' => 'red', - 'Exif' => array('type'=>'boolean','default'=>0), - 'Sequence' => null, - 'TotalEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'TotalEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'HourEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'HourEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'DayEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'DayEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'WeekEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'WeekEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'MonthEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'MonthEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'ArchivedEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'ArchivedEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'ZoneCount' => 0, - 'Refresh' => null, - 'DefaultCodec' => 'auto', - 'GroupIds' => array('default'=>array(), 'do_not_update'=>1), -); -private $status_fields = array( - 'Status' => null, - 'AnalysisFPS' => null, - 'CaptureFPS' => null, - 'CaptureBandwidth' => null, -); + protected $defaults = array( + 'Id' => null, + 'Name' => '', + 'Notes' => '', + 'ServerId' => 0, + 'StorageId' => 0, + 'Type' => 'Ffmpeg', + 'Function' => 'Mocord', + 'Enabled' => array('type'=>'boolean','default'=>1), + 'LinkedMonitors' => array('type'=>'set', 'default'=>null), + 'Triggers' => array('type'=>'set','default'=>''), + 'Device' => '', + 'Channel' => 0, + 'Format' => '0', + 'V4LMultiBuffer' => null, + 'V4LCapturesPerFrame' => 1, + 'Protocol' => null, + 'Method' => '', + 'Host' => null, + 'Port' => '', + 'SubPath' => '', + 'Path' => null, + 'Options' => null, + 'User' => null, + 'Pass' => null, + // These are NOT NULL default 0 in the db, but 0 is not a valid value. FIXME + 'Width' => null, + 'Height' => null, + 'Colours' => 4, + 'Palette' => '0', + 'Orientation' => null, + 'Deinterlacing' => 0, + 'DecoderHWAccelName' => null, + 'DecoderHWAccelDevice' => null, + 'SaveJPEGs' => 3, + 'VideoWriter' => '0', + 'OutputCodec' => null, + 'OutputContainer' => null, + 'EncoderParameters' => "# Lines beginning with # are a comment \n# For changing quality, use the crf option\n# 1 is best, 51 is worst quality\n#crf=23\n", + 'RecordAudio' => array('type'=>'boolean', 'default'=>0), + 'RTSPDescribe' => array('type'=>'boolean','default'=>0), + 'Brightness' => -1, + 'Contrast' => -1, + 'Hue' => -1, + 'Colour' => -1, + 'EventPrefix' => 'Event-', + 'LabelFormat' => '%N - %d/%m/%y %H:%M:%S', + 'LabelX' => 0, + 'LabelY' => 0, + 'LabelSize' => 1, + 'ImageBufferCount' => 20, + 'WarmupCount' => 0, + 'PreEventCount' => 5, + 'PostEventCount' => 5, + 'StreamReplayBuffer' => 0, + 'AlarmFrameCount' => 1, + 'SectionLength' => 600, + 'MinSectionLength' => 10, + 'FrameSkip' => 0, + 'MotionFrameSkip' => 0, + 'AnalysisFPSLimit' => null, + 'AnalysisUpdateDelay' => 0, + 'MaxFPS' => null, + 'AlarmMaxFPS' => null, + 'FPSReportInterval' => 100, + 'RefBlendPerc' => 6, + 'AlarmRefBlendPerc' => 6, + 'Controllable' => array('type'=>'boolean','default'=>0), + 'ControlId' => null, + 'ControlDevice' => null, + 'ControlAddress' => null, + 'AutoStopTimeout' => null, + 'TrackMotion' => array('type'=>'boolean','default'=>0), + 'TrackDelay' => null, + 'ReturnLocation' => -1, + 'ReturnDelay' => null, + 'DefaultRate' => 100, + 'DefaultScale' => 100, + 'SignalCheckPoints' => 0, + 'SignalCheckColour' => '#0000BE', + 'WebColour' => '#ff0000', + 'Exif' => array('type'=>'boolean','default'=>0), + 'Sequence' => null, + 'TotalEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'TotalEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'HourEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'HourEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'DayEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'DayEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'WeekEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'WeekEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'MonthEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'MonthEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'ArchivedEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'ArchivedEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'ZoneCount' => 0, + 'Refresh' => null, + 'DefaultCodec' => 'auto', + 'GroupIds' => array('default'=>array(), 'do_not_update'=>1), + ); + private $status_fields = array( + 'Status' => null, + 'AnalysisFPS' => null, + 'CaptureFPS' => null, + 'CaptureBandwidth' => null, + ); public function Control() { - if ( !array_key_exists('Control', $this) ) { + if ( !property_exists($this, 'Control') ) { if ( $this->ControlId() ) $this->{'Control'} = Control::find_one(array('Id'=>$this->{'ControlId'})); - if ( !(array_key_exists('Control', $this) and $this->{'Control'}) ) + if ( !(property_exists($this, 'Control') and $this->{'Control'}) ) $this->{'Control'} = new Control(); } return $this->{'Control'}; @@ -138,7 +139,7 @@ private $status_fields = array( $this->{$fn} = $args[0]; } } - if ( array_key_exists($fn, $this) ) { + if ( property_exists($this, $fn) ) { return $this->{$fn}; } else if ( array_key_exists($fn, $this->defaults) ) { if ( is_array($this->defaults[$fn]) ) { @@ -211,9 +212,9 @@ private $status_fields = array( $this->{'Width'} = $new; $field = ( $this->Orientation() == 'ROTATE_90' or $this->Orientation() == 'ROTATE_270' ) ? 'Height' : 'Width'; - if ( array_key_exists($field, $this) ) + if ( property_exists($this, $field) ) return $this->{$field}; - return $this->defaults{$field}; + return $this->defaults[$field]; } // end function Width public function ViewHeight($new=null) { @@ -221,9 +222,9 @@ private $status_fields = array( $this->{'Height'} = $new; $field = ( $this->Orientation() == 'ROTATE_90' or $this->Orientation() == 'ROTATE_270' ) ? 'Width' : 'Height'; - if ( array_key_exists($field, $this) ) + if ( property_exists($this, $field) ) return $this->{$field}; - return $this->defaults{$field}; + return $this->defaults[$field]; } // end function Height public function SignalCheckColour($new=null) { @@ -234,10 +235,10 @@ private $status_fields = array( // Validate that it's a valid colour (we seem to allow color names, not just hex). // This also helps prevent XSS. - if (array_key_exists($field, $this) && preg_match('/^[#0-9a-zA-Z]+$/', $this->{$field})) { + if ( property_exists($this, $field) && preg_match('/^[#0-9a-zA-Z]+$/', $this->{$field})) { return $this->{$field}; } - return $this->defaults{$field}; + return $this->defaults[$field]; } // end function SignalCheckColour public static function find( $parameters = array(), $options = array() ) { @@ -253,7 +254,7 @@ private $status_fields = array( Warning('Attempt to control a monitor with no Id'); return; } - if ( (!defined('ZM_SERVER_ID')) or ( array_key_exists('ServerId', $this) and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) { + if ( (!defined('ZM_SERVER_ID')) or ( property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) { if ( $this->Type() == 'Local' ) { $zmcArgs = '-d '.$this->{'Device'}; } else { @@ -306,7 +307,7 @@ private $status_fields = array( return; } - if ( (!defined('ZM_SERVER_ID')) or ( array_key_exists('ServerId', $this) and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) { + if ( (!defined('ZM_SERVER_ID')) or ( property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) { if ( $this->{'Function'} == 'None' || $this->{'Function'} == 'Monitor' || $mode == 'stop' ) { if ( ZM_OPT_CONTROL ) { daemonControl('stop', 'zmtrack.pl', '-m '.$this->{'Id'}); @@ -367,8 +368,8 @@ private $status_fields = array( } } - if ( !array_key_exists('GroupIds', $this) ) { - if ( array_key_exists('Id', $this) and $this->{'Id'} ) { + if ( !property_exists($this, 'GroupIds') ) { + if ( property_exists($this, 'Id') and $this->{'Id'} ) { $this->{'GroupIds'} = dbFetchAll('SELECT `GroupId` FROM `Groups_Monitors` WHERE `MonitorId`=?', 'GroupId', array($this->{'Id'}) ); if ( ! $this->{'GroupIds'} ) $this->{'GroupIds'} = array(); @@ -417,7 +418,7 @@ private $status_fields = array( if ( $new ) { $this->{'Storage'} = $new; } - if ( ! ( array_key_exists('Storage', $this) and $this->{'Storage'} ) ) { + if ( ! ( property_exists($this, 'Storage') and $this->{'Storage'} ) ) { $this->{'Storage'} = isset($this->{'StorageId'}) ? Storage::find_one(array('Id'=>$this->{'StorageId'})) : new Storage(NULL); @@ -467,58 +468,58 @@ private $status_fields = array( return $source; } // end function Source - public function UrlToIndex() { - return $this->Server()->UrlToIndex(); + public function UrlToIndex($port=null) { + return $this->Server()->UrlToIndex($port); //ZM_MIN_STREAMING_PORT ? (ZM_MIN_STREAMING_PORT+$this->Id()) : null); } -public function sendControlCommand($command) { - // command is generally a command option list like --command=blah but might be just the word quit + public function sendControlCommand($command) { + // command is generally a command option list like --command=blah but might be just the word quit - $options = array(); - # Convert from a command line params to an option array - foreach ( explode(' ', $command) as $option ) { - if ( preg_match('/--([^=]+)(?:=(.+))?/', $option, $matches) ) { - $options[$matches[1]] = $matches[2]?$matches[2]:1; - } else if ( $option != '' and $option != 'quit' ) { - Warning("Ignored command for zmcontrol $option in $command"); - } - } - if ( !count($options) ) { - if ( $command == 'quit' ) { - $options['command'] = 'quit'; - } else { - Warning("No commands to send to zmcontrol from $command"); - return false; - } - } - - if ( (!defined('ZM_SERVER_ID')) or ( array_key_exists('ServerId', $this) and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) { - # Local - Logger::Debug('Trying to send options ' . print_r($options, true)); - - $optionString = jsonEncode($options); - Logger::Debug("Trying to send options $optionString"); - // Either connects to running zmcontrol.pl or runs zmcontrol.pl to send the command. - $socket = socket_create(AF_UNIX, SOCK_STREAM, 0); - if ( $socket < 0 ) { - Error('socket_create() failed: '.socket_strerror($socket)); - return false; - } - $sockFile = ZM_PATH_SOCKS.'/zmcontrol-'.$this->{'Id'}.'.sock'; - if ( @socket_connect($socket, $sockFile) ) { - if ( !socket_write($socket, $optionString) ) { - Error('Can\'t write to control socket: '.socket_strerror(socket_last_error($socket))); - return false; + $options = array(); + # Convert from a command line params to an option array + foreach ( explode(' ', $command) as $option ) { + if ( preg_match('/--([^=]+)(?:=(.+))?/', $option, $matches) ) { + $options[$matches[1]] = $matches[2]?$matches[2]:1; + } else if ( $option != '' and $option != 'quit' ) { + Warning("Ignored command for zmcontrol $option in $command"); } - } else if ( $command != 'quit' ) { - $command = ZM_PATH_BIN.'/zmcontrol.pl '.$command.' --id='.$this->{'Id'}; - - // Can't connect so use script - $ctrlOutput = exec(escapeshellcmd($command)); } - socket_close($socket); - } else if ( $this->ServerId() ) { + if ( !count($options) ) { + if ( $command == 'quit' ) { + $options['command'] = 'quit'; + } else { + Warning("No commands to send to zmcontrol from $command"); + return false; + } + } + + if ( (!defined('ZM_SERVER_ID')) or ( property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) { + # Local + Logger::Debug('Trying to send options ' . print_r($options, true)); + + $optionString = jsonEncode($options); + Logger::Debug("Trying to send options $optionString"); + // Either connects to running zmcontrol.pl or runs zmcontrol.pl to send the command. + $socket = socket_create(AF_UNIX, SOCK_STREAM, 0); + if ( $socket < 0 ) { + Error('socket_create() failed: '.socket_strerror($socket)); + return false; + } + $sockFile = ZM_PATH_SOCKS.'/zmcontrol-'.$this->{'Id'}.'.sock'; + if ( @socket_connect($socket, $sockFile) ) { + if ( !socket_write($socket, $optionString) ) { + Error('Can\'t write to control socket: '.socket_strerror(socket_last_error($socket))); + return false; + } + } else if ( $command != 'quit' ) { + $command = ZM_PATH_BIN.'/zmcontrol.pl '.$command.' --id='.$this->{'Id'}; + + // Can't connect so use script + $ctrlOutput = exec(escapeshellcmd($command)); + } + socket_close($socket); + } else if ( $this->ServerId() ) { $Server = $this->Server(); $url = ZM_BASE_PROTOCOL . '://'.$Server->Hostname().'/zm/api/monitors/daemonControl/'.$this->{'Id'}.'/'.$mode.'/zmcontrol.json'; diff --git a/web/includes/MontageLayout.php b/web/includes/MontageLayout.php index ca6b17ef7..f67f3ace5 100644 --- a/web/includes/MontageLayout.php +++ b/web/includes/MontageLayout.php @@ -1,133 +1,22 @@ null, 'Name' => '', 'Positions' => 0, ); - public function __construct( $IdOrRow = NULL ) { - if ( $IdOrRow ) { - $row = NULL; - if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) { - $row = dbFetchOne( 'SELECT * FROM MontageLayouts WHERE Id=?', NULL, array( $IdOrRow ) ); - if ( ! $row ) { - Error("Unable to load MontageLayout record for Id=" . $IdOrRow ); - } - } else if ( is_array( $IdOrRow ) ) { - $row = $IdOrRow; - } else { - Error("Unknown argument passed to MontageLayout Constructor ($IdOrRow)"); - return; - } - - if ( $row ) { - foreach ($row as $k => $v) { - $this->{$k} = $v; - } - } else { - Error('No row for MontageLayout ' . $IdOrRow ); - } - } # end if isset($IdOrRow) - } // end function __construct - - public function __call($fn, array $args){ - if ( count($args) ) { - $this->{$fn} = $args[0]; - } - if ( array_key_exists($fn, $this) ) { - return $this->{$fn}; - } else { - if ( array_key_exists( $fn, $this->defaults ) ) { - return $this->defaults{$fn}; - } else { - $backTrace = debug_backtrace(); - $file = $backTrace[1]['file']; - $line = $backTrace[1]['line']; - Warning( "Unknown function call MontageLayout->$fn from $file:$line" ); - } - } + public static function find( $parameters = array(), $options = array() ) { + return ZM_Object::_find(get_class(), $parameters, $options); } - public function set( $data ) { - foreach ($data as $k => $v) { - if ( is_array( $v ) ) { - # perhaps should turn into a comma-separated string - $this->{$k} = implode(',',$v); - } else if ( is_string( $v ) ) { - $this->{$k} = trim( $v ); - } else if ( is_integer( $v ) ) { - $this->{$k} = $v; - } else if ( is_bool( $v ) ) { - $this->{$k} = $v; - } else { - Error( "Unknown type $k => $v of var " . gettype( $v ) ); - $this->{$k} = $v; - } - } + public static function find_one( $parameters = array(), $options = array() ) { + return ZM_Object::_find_one(get_class(), $parameters, $options); } - public static function find( $parameters = null, $options = null ) { - $filters = array(); - $sql = 'SELECT * FROM MontageLayouts '; - $values = array(); - - if ( $parameters ) { - $fields = array(); - $sql .= 'WHERE '; - foreach ( $parameters as $field => $value ) { - if ( $value == null ) { - $fields[] = $field.' IS NULL'; - } else if ( is_array( $value ) ) { - $func = function(){return '?';}; - $fields[] = $field.' IN ('.implode(',', array_map( $func, $value ) ). ')'; - $values += $value; - - } else { - $fields[] = $field.'=?'; - $values[] = $value; - } - } - $sql .= implode(' AND ', $fields ); - } - if ( $options and isset($options['order']) ) { - $sql .= ' ORDER BY ' . $options['order']; - } - $result = dbQuery($sql, $values); - if ( $result ) { - $results = $result->fetchALL(); - foreach ( $results as $row ) { - $filters[] = new MontageLayout($row); - } - } - return $filters; - } - public function save( $new_values = null ) { - if ( $new_values ) { - foreach ( $new_values as $k=>$v ) { - $this->{$k} = $v; - } - } - - $fields = array_values( array_filter( array_keys($this->defaults), function($field){return $field != 'Id';} ) ); - $values = null; - if ( isset($this->{'Id'}) ) { - $sql = 'UPDATE MontageLayouts SET '.implode(', ', array_map( function($field) {return $field.'=?';}, $fields ) ) . ' WHERE Id=?'; - $values = array_map( function($field){return $this->{$field};}, $fields ); - $values[] = $this->{'Id'}; - dbQuery($sql, $values); - } else { - $sql = 'INSERT INTO MontageLayouts ('.implode( ',', $fields ).') VALUES ('.implode(',',array_map( function(){return '?';}, $fields ) ).')'; - $values = array_map( function($field){return $this->{$field};}, $fields ); - dbQuery($sql, $values); - global $dbConn; - $this->{'Id'} = $dbConn->lastInsertId(); - } - } // end function save } // end class MontageLayout ?> diff --git a/web/includes/Object.php b/web/includes/Object.php index d51cf082d..1c17ce312 100644 --- a/web/includes/Object.php +++ b/web/includes/Object.php @@ -45,7 +45,7 @@ class ZM_Object { $this->{$fn} = $args[0]; } - if ( array_key_exists($fn, $this) ) { + if ( property_exists($this, $fn) ) { return $this->{$fn}; } else { if ( array_key_exists($fn, $this->defaults) ) { @@ -140,10 +140,10 @@ class ZM_Object { foreach ($this->defaults as $key => $value) { if ( is_callable(array($this, $key)) ) { $json[$key] = $this->$key(); - } else if ( array_key_exists($key, $this) ) { + } else if ( property_exists($this, $key) ) { $json[$key] = $this->{$key}; } else { - $json[$key] = $this->defaults{$key}; + $json[$key] = $this->defaults[$key]; } } return json_encode($json); @@ -158,14 +158,24 @@ class ZM_Object { # perhaps should turn into a comma-separated string $this->{$k} = implode(',', $v); } else if ( is_string($v) ) { - if ( $v == '' and array_key_exists($k, $this->defaults) ) { - if ( is_array($this->defaults[$k]) ) +if ( 0 ) { +# Remarking this out. We are setting a value, not asking for a default to be set. +# So don't do defaults here, do them somewhere else + if ( ($v == null) and array_key_exists($k, $this->defaults) ) { +Logger::Debug("$k => Have default for $v: "); + if ( is_array($this->defaults[$k]) ) { $this->{$k} = $this->defaults[$k]['default']; - else - $this->{$k} = $this->defaults[$k]; - } else { - $this->{$k} = trim($v); + } else { + $this->{$k} = $this->defaults[$k]; + Logger::Debug("$k => Have default for $v: " . $this->{$k}); + } + } else { + $this->{$k} = trim($v); } +} else { + $this->{$k} = trim($v); +} + } else if ( is_integer($v) ) { $this->{$k} = $v; } else if ( is_bool($v) ) { @@ -215,7 +225,7 @@ class ZM_Object { } else if ( $this->$field() != $value ) { $changes[$field] = $value; } - } else if ( array_key_exists($field, $this) ) { + } else if ( property_exists($this, $field) ) { $type = (array_key_exists($field, $this->defaults) && is_array($this->defaults[$field])) ? $this->defaults[$field]['type'] : 'scalar'; Logger::Debug("Checking field $field => current ". (is_array($this->{$field}) ? implode(',',$this->{$field}) : $this->{$field}) . ' ?= ' . diff --git a/web/includes/Storage.php b/web/includes/Storage.php index 01465de65..286cec8da 100644 --- a/web/includes/Storage.php +++ b/web/includes/Storage.php @@ -80,7 +80,7 @@ class Storage extends ZM_Object { } public function disk_total_space() { - if ( !array_key_exists('disk_total_space', $this) ) { + if ( !property_exists($this, 'disk_total_space') ) { $path = $this->Path(); if ( file_exists($path) ) { $this->{'disk_total_space'} = disk_total_space($path); @@ -94,7 +94,7 @@ class Storage extends ZM_Object { public function disk_used_space() { # This isn't a function like this in php, so we have to add up the space used in each event. - if ( ( !array_key_exists('disk_used_space', $this)) or !$this->{'disk_used_space'} ) { + if ( ( !property_exists($this, 'disk_used_space')) or !$this->{'disk_used_space'} ) { if ( $this->{'Type'} == 's3fs' ) { $this->{'disk_used_space'} = $this->event_disk_space(); } else { @@ -112,7 +112,7 @@ class Storage extends ZM_Object { public function event_disk_space() { # This isn't a function like this in php, so we have to add up the space used in each event. - if ( (! array_key_exists('DiskSpace', $this)) or (!$this->{'DiskSpace'}) ) { + if ( (! property_exists($this, 'DiskSpace')) or (!$this->{'DiskSpace'}) ) { $used = dbFetchOne('SELECT SUM(DiskSpace) AS DiskSpace FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL', 'DiskSpace', array($this->Id())); do { @@ -130,8 +130,8 @@ class Storage extends ZM_Object { } // end function event_disk_space public function Server() { - if ( ! array_key_exists('Server',$this) ) { - if ( array_key_exists('ServerId', $this) ) { + if ( ! property_exists($this, 'Server') ) { + if ( property_exists($this, 'ServerId') ) { $this->{'Server'} = Server::find_one(array('Id'=>$this->{'ServerId'})); if ( !$this->{'Server'} ) { diff --git a/web/includes/actions/controlcap.php b/web/includes/actions/controlcap.php index eec3ffd8b..ad4985f11 100644 --- a/web/includes/actions/controlcap.php +++ b/web/includes/actions/controlcap.php @@ -28,6 +28,65 @@ if ( $action == 'controlcap' ) { require_once('includes/Control.php'); $Control = new ZM\Control( !empty($_REQUEST['cid']) ? $_REQUEST['cid'] : null ); + $field_defaults = array( + 'CanWake' => 0, + 'CanSleep' => 0, + 'CanReset' => 0, + 'CanReboot' => 0, + 'CanMove' => 0, + 'CanMoveDiag' => 0, + 'CanMoveMap' => 0, + 'CanMoveRel' => 0, + 'CanMoveAbs' => 0, + 'CanMoveCon' => 0, + 'CanPan' => 0, + 'HasPanSpeed' => 0, + 'HasTurboPan' => 0, + 'CanTilt' => 0, + 'HasTiltSpeed' => 0, + 'HasTurboTilt' => 0, + 'CanZoom' => 0, + 'CanZoomRel' => 0, + 'CanZoomAbs' => 0, + 'CanZoomCon' => 0, + 'HasZoomSpeed' => 0, + 'CanFocus' => 0, + 'CanAutoFocus' => 0, + 'CanFocusRel' => 0, + 'CanFocusAbs' => 0, + 'CanFocusCon' => 0, + 'HasFocusSpeed' => 0, + 'CanGain' => 0, + 'CanAutoGain' => 0, + 'CanGainRel' => 0, + 'CanGainAbs' => 0, + 'CanGainCon' => 0, + 'HasGainSpeed' => 0, + 'CanWhite' => 0, + 'CanAutoWhite' => 0, + 'CanWhiteRel' => 0, + 'CanWhiteAbs' => 0, + 'CanWhiteCon' => 0, + 'HasWhiteSpeed' => 0, + 'CanIris' => 0, + 'CanAutoIris' => 0, + 'CanIrisRel' => 0, + 'CanIrisAbs' => 0, + 'CanIrisCon' => 0, + 'HasIrisSpeed' => 0, + 'HasPresets' => 0, + 'HasHomePreset' => 0, + 'CanSetPresets' => 0, + ); + + # Checkboxes don't return an element in the POST data, so won't be present in newControl. + # So force a value for these fields + foreach ( $field_defaults as $field => $value ) { + if ( ! (isset($_REQUEST['newControl'][$field]) and $_REQUEST['newControl'][$field]) ) { + $_REQUEST['newControl'][$field] = $value; + } + } # end foreach type + //$changes = getFormChanges( $control, $_REQUEST['newControl'], $types, $columns ); $Control->save($_REQUEST['newControl']); $refreshParent = true; diff --git a/web/includes/actions/function.php b/web/includes/actions/function.php index ebdc416fc..00e4e21f7 100644 --- a/web/includes/actions/function.php +++ b/web/includes/actions/function.php @@ -39,7 +39,7 @@ if ( $action == 'function' ) { $oldFunction = $monitor['Function']; $oldEnabled = $monitor['Enabled']; if ( $newFunction != $oldFunction || $newEnabled != $oldEnabled ) { - dbQuery('UPDATE Monitors SET Function=?, Enabled=? WHERE Id=?', + dbQuery('UPDATE Monitors SET `Function`=?, `Enabled`=? WHERE `Id`=?', array($newFunction, $newEnabled, $mid)); $monitor['Function'] = $newFunction; diff --git a/web/includes/actions/state.php b/web/includes/actions/state.php index 9799cdec3..0f7a9e9a5 100644 --- a/web/includes/actions/state.php +++ b/web/includes/actions/state.php @@ -31,19 +31,19 @@ if ( $action == 'state' ) { } } else if ( $action == 'save' ) { if ( !empty($_REQUEST['runState']) || !empty($_REQUEST['newState']) ) { - $sql = 'SELECT Id,Function,Enabled FROM Monitors ORDER BY Id'; + $sql = 'SELECT `Id`,`Function`,`Enabled` FROM Monitors ORDER BY Id'; $definitions = array(); - foreach( dbFetchAll($sql) as $monitor ) { + foreach ( dbFetchAll($sql) as $monitor ) { $definitions[] = $monitor['Id'].':'.$monitor['Function'].':'.$monitor['Enabled']; } $definition = join(',', $definitions); if ( $_REQUEST['newState'] ) $_REQUEST['runState'] = $_REQUEST['newState']; - dbQuery('REPLACE INTO States SET Name=?, Definition=?', array($_REQUEST['runState'],$definition)); + dbQuery('REPLACE INTO `States` SET `Name`=?, `Definition`=?', array($_REQUEST['runState'],$definition)); } } else if ( $action == 'delete' ) { if ( isset($_REQUEST['runState']) ) - dbQuery('DELETE FROM States WHERE Name=?', array($_REQUEST['runState'])); + dbQuery('DELETE FROM `States` WHERE `Name`=?', array($_REQUEST['runState'])); } $view = 'console'; ?> diff --git a/web/includes/auth.php b/web/includes/auth.php index 4fc39d30e..f958463c3 100644 --- a/web/includes/auth.php +++ b/web/includes/auth.php @@ -220,19 +220,19 @@ function generateAuthHash($useRemoteAddr, $force=false) { function visibleMonitor($mid) { global $user; - return ( empty($user['MonitorIds']) || in_array($mid, explode(',', $user['MonitorIds'])) ); + return ( $user && empty($user['MonitorIds']) || in_array($mid, explode(',', $user['MonitorIds'])) ); } function canView($area, $mid=false) { global $user; - return ( ($user[$area] == 'View' || $user[$area] == 'Edit') && ( !$mid || visibleMonitor($mid) ) ); + return ( $user && ($user[$area] == 'View' || $user[$area] == 'Edit') && ( !$mid || visibleMonitor($mid) ) ); } function canEdit($area, $mid=false) { global $user; - return ( $user[$area] == 'Edit' && ( !$mid || visibleMonitor($mid) )); + return ( $user && ($user[$area] == 'Edit') && ( !$mid || visibleMonitor($mid) )); } function userFromSession() { diff --git a/web/includes/config.php.in b/web/includes/config.php.in index dd3439680..909a10e15 100644 --- a/web/includes/config.php.in +++ b/web/includes/config.php.in @@ -33,9 +33,9 @@ $configFile = 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" ); + print("Warning, overriding installed $localConfigFile file with local copy\n"); else - error_log( "Warning, overriding installed $localConfigFile file with local copy" ); + error_log("Warning, overriding installed $localConfigFile file with local copy"); $configFile = $localConfigFile; } @@ -49,19 +49,19 @@ if ( is_dir($configSubFolder) ) { if ( is_readable($configSubFolder) ) { foreach ( glob("$configSubFolder/*.conf") as $filename ) { //error_log("processing $filename"); - $configvals = array_replace($configvals, process_configfile($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." ); + error_log("WARNING: ZoneMinder configuration subfolder found but is not readable. Check folder permissions on $configSubFolder."); } } else { - error_log( "WARNING: ZoneMinder configuration subfolder found but is not a directory. Check $configSubFolder." ); + error_log("WARNING: ZoneMinder configuration subfolder found but is not a directory. Check $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 ); +foreach ( $configvals as $key => $value ) { + define($key, $value); } // @@ -135,8 +135,8 @@ define( 'SCALE_BASE', 100 ); // The additional scalin define( 'STRF_FMT_DATETIME_DB', '%Y-%m-%d %H:%M:%S' ); // Strftime format for database queries, don't change define( 'MYSQL_FMT_DATETIME_SHORT', '%y/%m/%d %H:%i:%S' ); // MySQL date_format shorter format for dates with time -require_once( 'database.php' ); -require_once( 'logger.php' ); +require_once('database.php'); +require_once('logger.php'); loadConfig(); ZM\Logger::fetch()->initialise(); @@ -165,28 +165,30 @@ function loadConfig( $defineConsts=true ) { $result = $dbConn->query('SELECT Name,Value FROM Config'); if ( !$result ) echo mysql_error(); - while( $row = dbFetchNext( $result ) ) { + while( $row = dbFetchNext($result) ) { if ( $defineConsts ) - define( $row['Name'], $row['Value'] ); + define($row['Name'], $row['Value']); $config[$row['Name']] = $row; } } # end function loadConfig // For Human-readability, use ZM_SERVER_HOST or ZM_SERVER_NAME in zm.conf, and convert it here to a ZM_SERVER_ID if ( ! defined('ZM_SERVER_ID') ) { + require_once('Server.php'); if ( defined('ZM_SERVER_NAME') and ZM_SERVER_NAME ) { - $server_id = dbFetchOne('SELECT Id FROM Servers WHERE Name=?', 'Id', array(ZM_SERVER_NAME)); - if ( ! $server_id ) { - Error('Invalid Multi-Server configration detected. ZM_SERVER_NAME set to ' . ZM_SERVER_NAME . ' in zm.conf, but no corresponding entry found in Servers table.'); + # Use Server lookup so that it caches + $Server = ZM\Server::find_one(array('Name'=>ZM_SERVER_NAME)); + if ( !$Server ) { + ZM\Error('Invalid Multi-Server configration detected. ZM_SERVER_NAME set to ' . ZM_SERVER_NAME . ' in zm.conf, but no corresponding entry found in Servers table.'); } else { - define( 'ZM_SERVER_ID', $server_id ); + define('ZM_SERVER_ID', $Server->Id()); } } else if ( defined('ZM_SERVER_HOST') and ZM_SERVER_HOST ) { - $server_id = dbFetchOne('SELECT Id FROM Servers WHERE Name=?', 'Id', array(ZM_SERVER_HOST)); - if ( ! $server_id ) { - Error('Invalid Multi-Server configration detected. ZM_SERVER_HOST set to ' . ZM_SERVER_HOST . ' in zm.conf, but no corresponding entry found in Servers table.'); + $Server = ZM\Server::find_one(array('Name'=>ZM_SERVER_HOST)); + if ( ! $Server ) { + ZM\Error('Invalid Multi-Server configration detected. ZM_SERVER_HOST set to ' . ZM_SERVER_HOST . ' in zm.conf, but no corresponding entry found in Servers table.'); } else { - define( 'ZM_SERVER_ID', $server_id ); + define('ZM_SERVER_ID', $Server->Id()); } } } @@ -197,21 +199,22 @@ function process_configfile($configFile) { if ( is_readable( $configFile ) ) { $configvals = array(); - $cfg = fopen( $configFile, 'r') or Error("Could not open config file: $configFile."); + $cfg = fopen($configFile, 'r') or ZM\Error("Could not open config file: $configFile."); while ( !feof($cfg) ) { - $str = fgets( $cfg, 256 ); - if ( preg_match( '/^\s*$/', $str )) + $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 )) + } else if ( preg_match( '/^\s*([^=\s]+)\s*=\s*[\'"]*(.*?)[\'"]*\s*$/', $str, $matches )) { $configvals[$matches[1]] = $matches[2]; + } else { + ZM\Error("Malformed line in config $configFile\n$str"); + } } - fclose( $cfg ); - return( $configvals ); + fclose($cfg); + return $configvals; } else { - error_log( "WARNING: ZoneMinder configuration file found but is not readable. Check file permissions on $configFile." ); - return( false ); + error_log("WARNING: ZoneMinder configuration file found but is not readable. Check file permissions on $configFile."); + return false; } } diff --git a/web/includes/database.php b/web/includes/database.php index eab70f47f..d0124656d 100644 --- a/web/includes/database.php +++ b/web/includes/database.php @@ -110,16 +110,10 @@ function dbError($sql) { function dbEscape( $string ) { global $dbConn; - if ( version_compare(phpversion(), '4.3.0', '<')) - if ( get_magic_quotes_gpc() ) - return $dbConn->quote(stripslashes($string)); - else - return $dbConn->quote($string); + if ( version_compare(phpversion(), '5.4', '<=') and get_magic_quotes_gpc() ) + return $dbConn->quote(stripslashes($string)); else - if ( get_magic_quotes_gpc() ) - return $dbConn->quote(stripslashes($string)); - else - return $dbConn->quote($string); + return $dbConn->quote($string); } function dbQuery($sql, $params=NULL) { @@ -212,6 +206,10 @@ function dbFetch($sql, $col=false) { } function dbFetchNext($result, $col=false) { + if ( !$result ) { + ZM\Error("dbFetchNext called on null result."); + return false; + } if ( $dbRow = $result->fetch(PDO::FETCH_ASSOC) ) return $col ? $dbRow[$col] : $dbRow; return false; diff --git a/web/includes/functions.php b/web/includes/functions.php index aea0c8b14..42dc56c30 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -409,6 +409,11 @@ ZM\Logger::Debug("Event type: " . gettype($event)); global $user; + if ( $event->Archived() ) { + ZM\Info('Cannot delete Archived event.'); + return; + } # end if Archived + if ( $user['Events'] == 'Edit' ) { $event->delete(); } # CAN EDIT @@ -1085,9 +1090,8 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') { $terms = isset($filter['Query']) ? $filter['Query']['terms'] : NULL; if ( !isset($terms) ) { $backTrace = debug_backtrace(); - $file = $backTrace[1]['file']; - $line = $backTrace[1]['line']; - ZM\Warning("No terms in filter from $file:$line"); + ZM\Warning('No terms in filter'); + ZM\Warning(print_r($backTrace, true)); ZM\Warning(print_r($filter, true)); } if ( isset($terms) && count($terms) ) { @@ -1324,7 +1328,7 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') { $filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][val]").'='.urlencode($term['val']); $filter['fields'] .= "\n"; } - } // end if ( isset($term['attr']) ) + } // end if isset($term['attr']) if ( isset($term['cbr']) && (string)(int)$term['cbr'] == $term['cbr'] ) { $filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][cbr]").'='.urlencode($term['cbr']); $filter['sql'] .= ' '.str_repeat(')', $term['cbr']).' '; @@ -1336,6 +1340,8 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') { if ( $saveToSession ) { $_SESSION['filter'] = $filter; } + } else { + $filter['query'] = $querySep.urlencode('filter[Query][terms]=[]'); } // end if terms #if ( 0 ) { @@ -2604,4 +2610,12 @@ function html_radio($name, $values, $selected=null, $options=array(), $attrs=arr return $html; } # end sub html_radio + +function random_colour() { + return '#'. + str_pad( dechex( mt_rand( 0, 255 ) ), 2, '0', STR_PAD_LEFT). + str_pad( dechex( mt_rand( 0, 255 ) ), 2, '0', STR_PAD_LEFT). + str_pad( dechex( mt_rand( 0, 255 ) ), 2, '0', STR_PAD_LEFT); +} + ?> diff --git a/web/skins/classic/css/base/views/controlcap.css b/web/skins/classic/css/base/views/controlcap.css new file mode 100644 index 000000000..ed9a664e7 --- /dev/null +++ b/web/skins/classic/css/base/views/controlcap.css @@ -0,0 +1,4 @@ + +input[type="number"] { + width: 70px; +} diff --git a/web/skins/classic/css/base/views/controlcaps.css b/web/skins/classic/css/base/views/controlcaps.css index 1a2783fd7..d860d88ae 100644 --- a/web/skins/classic/css/base/views/controlcaps.css +++ b/web/skins/classic/css/base/views/controlcaps.css @@ -1,3 +1,9 @@ -#content table.major .colCanMove, #content table.major .colCanZoom, #content table.major .colCanFocus, #content table.major .colCanIris, #content table.major .colCanWhiteBal, #content table.major .colHasPresets { +#content table.major .colCanMove, +#content table.major .colCanZoom, +#content table.major .colCanFocus, +#content table.major .colCanIris, +#content table.major .colCanWhiteBal, +#content table.major .colHasPresets { text-align: center; } + diff --git a/web/skins/classic/css/base/views/monitor.css b/web/skins/classic/css/base/views/monitor.css index 2b41e7d06..217b2f374 100644 --- a/web/skins/classic/css/base/views/monitor.css +++ b/web/skins/classic/css/base/views/monitor.css @@ -9,6 +9,10 @@ width: 100%; } +textarea, +input[name="newMonitor[Name]"] { + width: 100%; +} input[name="newMonitor[Width]"], input[name="newMonitor[Height]"] { width: 80px; diff --git a/web/skins/classic/includes/export_functions.php b/web/skins/classic/includes/export_functions.php index 02d0d86dd..8ce0fba5a 100644 --- a/web/skins/classic/includes/export_functions.php +++ b/web/skins/classic/includes/export_functions.php @@ -113,17 +113,17 @@ function exportEventDetail($event, $exportFrames, $exportImages) { - + - + - +
Id() ?>
Name()) ?>
MonitorName()) ?> (MonitorId() ?>)
Monitor()->Name()) ?> (MonitorId() ?>)
Cause()) ?>
Notes()) ?>
StartTime()) ) ?>
StartTime())) ?>
Length() ?>
Frames() ?>
AlarmFrames() ?>
TotScore() ?>
AvgScore() ?>
MaxScore() ?>
Archived()?translate('Yes'):translate('No') ?>
Archived()?'Yes':'No') ?>
@@ -226,13 +226,13 @@ function exportEventImages($event, $exportDetail, $exportFrames, $myfilelist) { ?> @@ -247,7 +247,7 @@ function exportEventImages($event, $exportDetail, $exportFrames, $myfilelist) { if ( $Monitor->VideoWriter() == '2' ) { # Passthrough $Rotation = $event->Orientation(); - if ( in_array($event->Orientation(),array('90','270')) ) + if ( in_array($event->Orientation(),array('ROTATE_90','ROTATE_270')) ) $Zoom = $event->Height()/$event->Width(); } ?> @@ -280,7 +280,7 @@ function exportEventImages($event, $exportDetail, $exportFrames, $myfilelist) {
 
 
+ onmousedown="slide(event,'horizontal', Width()-20)?>, 1, , ,0, 'imageslider_display_id');"> 
@@ -618,10 +618,10 @@ function exportEventImagesMaster($eids) {

Master

$eids)); + + foreach ($events as $event) { //get monitor id and event id - $event = new ZM\Event($eid); $eventMonitorId[$eid] = $event->MonitorId(); $eventPath[$eid] = $event->Relative_Path(); } @@ -653,20 +653,18 @@ function exportEventImagesMaster($eids) {

All

"; echo '

Monitor: ' . $monitorNames[$monitor_id] . '

'; - foreach ( $eids as $eid ) { - $Event = new ZM\Event($eid); - if ( $Event->MonitorId() == $monitor_id ) { - eventlist_html($Event); + foreach ( $events as $event ) { + if ( $event->MonitorId() == $monitor_id ) { + eventlist_html($event); } # end if its the right monitor } # end foreach event echo ''; @@ -780,7 +778,7 @@ function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $ex } fwrite($fp, exportEventDetail($event, $exportFrames, $exportImages)); fclose($fp); - $exportFileList[$file] = $event->Id().'/'.$file; + $exportFileList[$file] = $file; } if ( $exportFrames ) { $file = 'zmEventFrames.html'; @@ -789,7 +787,7 @@ function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $ex } fwrite($fp, exportEventFrames($event, $exportDetail, $exportImages)); fclose($fp); - $exportFileList[$file] = $event->Id().'/'.$file; + $exportFileList[$file] = $file; } if ( $exportImages ) { @@ -797,7 +795,7 @@ function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $ex $myfilelist = array(); foreach ( $files as $file ) { if ( preg_match('/-(?:capture|analyse).jpg$/', $file ) ) { - $myfilelist[$file] = $exportFileList[$file] = $event->Id().'/'.$file; + $myfilelist[$file] = $exportFileList[$file] = $file; } else { $filesLeft[$file] = $file; } @@ -806,12 +804,12 @@ function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $ex // create an image slider if ( !empty($myfilelist) ) { - $file = $event->Id().'/zmEventImages.html'; + $file = 'zmEventImages.html'; if ( !($fp = fopen($file, 'w')) ) ZM\Fatal("Can't open event images export file '$file'"); fwrite($fp, exportEventImages($event, $exportDetail, $exportFrames, $myfilelist)); fclose($fp); - $exportFileList[$file] = $event->Id().'/'.$file; + $exportFileList[$file] = $file; } } # end if exportImages @@ -819,7 +817,7 @@ function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $ex $filesLeft = array(); foreach ( $files as $file ) { if ( preg_match('/\.(?:mpg|mpeg|mov|swf|mp4|mkv|avi|asf|3gp)$/', $file) ) { - $exportFileList[$file] = $event->Id().'/'.$file; + $exportFileList[$file] = $file; } else { $filesLeft[$file] = $file; } @@ -829,7 +827,7 @@ function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $ex if ( $exportMisc ) { foreach ( $files as $file ) { - $exportFileList[$file] = $event->Id().'/'.$file; + $exportFileList[$file] = $file; } $files = array(); } @@ -850,10 +848,10 @@ function exportEvents( ) { if ( !canView('Events') ) { - ZM\Error("You do not have permission to view events."); + ZM\Error('You do not have permission to view events.'); return false; } else if ( empty($eids) ) { - ZM\Error("Attempt to export an empty list of events."); + ZM\Error('Attempt to export an empty list of events.'); return false; } @@ -863,23 +861,22 @@ function exportEvents( } # Ensure that we are going to be able to do this. - if ( ! file_exists(ZM_DIR_EXPORTS) ) { - if ( ! mkdir(ZM_DIR_EXPORTS) ) { - ZM\Fatal("Can't create exports dir at '".ZM_DIR_EXPORTS."'"); - } + if ( ! ( mkdir(ZM_DIR_EXPORTS) or file_exists(ZM_DIR_EXPORTS) ) ) { + ZM\Fatal('Can\'t create exports dir at \''.ZM_DIR_EXPORTS.'\''); } + chmod(ZM_DIR_EXPORTS, 0700); $export_dir = ZM_DIR_EXPORTS.'/zmExport_'.$connkey; # Ensure that we are going to be able to do this. - if ( ! file_exists($export_dir) ) { - if ( ! mkdir($export_dir) ) { - ZM\Fatal("Can't create exports dir at '$export_dir'"); - } else { - ZM\Logger::Debug("Successfully created dir '$export_dir'"); - } + if ( ! ( mkdir($export_dir) or file_exists($export_dir) ) ) { + ZM\Fatal("Can't create exports dir at '$export_dir'"); + } else { + ZM\Logger::Debug("Successfully created dir '$export_dir'"); } - if ( !chdir($export_dir) ) + chmod($export_dir, 0700); + if ( !chdir($export_dir) ) { ZM\Fatal("Can't chdir to $export_dir"); + } $export_root = 'zmExport'; $export_listFile = 'zmFileList.txt'; @@ -889,27 +886,30 @@ function exportEvents( if ( !is_array($eids) ) { $eids = array($eids); } - ZM\Logger::Debug("Eids: " . print_r($eids,true)); + ZM\Logger::Debug('Eids: ' . print_r($eids,true)); foreach ( $eids as $eid ) { $event = new ZM\Event($eid); $event_dir = $export_dir.'/'.$event->Id(); - if ( !mkdir($event_dir) ) + if ( !(mkdir($event_dir) or file_exists($event_dir) ) ) { ZM\Error("Can't mkdir $event_dir"); + } $event_exportFileList = exportFileList($event, $exportDetail, $exportFrames, $exportImages, $exportVideo, $exportMisc); ZM\Logger::Debug("File list for event $eid " . print_r($event_exportFileList, true)); $exportFileList = array_merge($exportFileList,$event_exportFileList); foreach ( $event_exportFileList as $file ) { - if ( preg_match('/\.html$/', $file ) ) + if ( preg_match('/\.html$/', $file) ) continue; - ZM\Logger::Debug('cp -as '.$event->Path().'/../'.$file.' '.$export_dir.'/'.$file); - exec('cp -as '.$event->Path().'/../'.$file.' '.$export_dir.'/'.$file); + #exec('cp -as '.$event->Path().'/../'.$file.' '.$export_dir.'/'.$file, $output, $return); + $cmd = 'cp -as '.$event->Path().'/'.$file.' '.$export_dir.'/'.$event->Id().'/'.$file. ' 2>&1'; + exec($cmd, $output, $return); + ZM\Logger::Debug($cmd.' return code: '.$return.' output: '.print_r($output,true)); } - } + } # end foreach event // create an master image if ( $exportImages ) { if ( !symlink(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/js/jquery.js', $export_dir.'/jquery.js') ) - ZM\Error("Failed linking jquery.js"); + ZM\Error('Failed linking jquery.js'); //if ( !symlink(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/js/video.js', $export_dir.'/video.js') ) //Error("Failed linking video.js"); @@ -960,12 +960,12 @@ function exportEvents( @unlink($archive); $command .= ' zmExport_' . $connkey.'/'; - ZM\Logger::Debug("Command is $command"); exec($command, $output, $status); if ( $status ) { ZM\Error("Command '$command' returned with status $status"); - if ( isset($output[0]) ) - ZM\Error("First line of output is '".$output[0]."'"); + if ( isset($output[0]) ) { + ZM\Error('First line of output is \''.$output[0].'\''); + } return false; } diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index 5abd519cf..60558f8e8 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -389,8 +389,8 @@ if ( (!ZM_OPT_USE_AUTH) or $user ) {
  • trending_up :
  • storage 90 ? ' class="warning"' : '' ).'>'.translate('DB').':'.$connections.'/'.$max_connections.''; ?> @@ -398,8 +398,12 @@ if ( (!ZM_OPT_USE_AUTH) or $user ) { Path()] = $area; + if ( ! $area->ServerId() ) { + $storage_areas_with_no_server_id[] = $area; + } } $func = function($S){ $class = ''; @@ -415,9 +419,9 @@ if ( (!ZM_OPT_USE_AUTH) or $user ) { '; }; #$func = function($S){ return ''.$S->Name() . ': ' . $S->disk_usage_percent().'%' . ''; }; if ( count($storage_areas) > 4 ) - $storage_areas = ZM\Storage::find( array('ServerId'=>null) ); + $storage_areas = $storage_areas_with_no_server_id; if ( count($storage_areas) <= 4 ) - echo implode( ', ', array_map ( $func, $storage_areas ) ); + echo implode(', ', array_map($func, $storage_areas)); echo ' ' . ZM_PATH_MAP .': '. getDiskPercent(ZM_PATH_MAP).'%'; ?> diff --git a/web/skins/classic/js/base.js b/web/skins/classic/js/base.js index 24f5d9a22..4ecc4c234 100644 --- a/web/skins/classic/js/base.js +++ b/web/skins/classic/js/base.js @@ -46,7 +46,7 @@ var popupSizes = { 'group': {'width': 760, 'height': 600}, 'groups': {'width': 540, 'height': 420}, 'image': {'addWidth': 48, 'addHeight': 80}, - 'log': {'width': 1080, 'height': 720}, + 'log': {'width': 1180, 'height': 720}, 'login': {'width': 720, 'height': 480}, 'logout': {'width': 260, 'height': 150}, 'monitor': {'width': 800, 'height': 780}, diff --git a/web/skins/classic/js/skin.js b/web/skins/classic/js/skin.js index 5ccddf96b..9e705bbc3 100644 --- a/web/skins/classic/js/skin.js +++ b/web/skins/classic/js/skin.js @@ -175,12 +175,13 @@ window.addEventListener("DOMContentLoaded", function onSkinDCL() { }); // 'data-on-click' calls the global function in the attribute value with no arguments when a click happens. - document.querySelectorAll("a[data-on-click], button[data-on-click], input[data-on-click]").forEach(function attachOnClick(el) { + document.querySelectorAll("i[data-on-click], a[data-on-click], button[data-on-click], input[data-on-click]").forEach(function attachOnClick(el) { var fnName = el.getAttribute("data-on-click"); if ( !window[fnName] ) { console.error("Nothing found to bind to " + fnName + " on element " + el.name); return; } + el.onclick = function() { window[fnName](); }; diff --git a/web/skins/classic/views/controlcap.php b/web/skins/classic/views/controlcap.php index bc48d05e4..68ab25b60 100644 --- a/web/skins/classic/views/controlcap.php +++ b/web/skins/classic/views/controlcap.php @@ -18,40 +18,31 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -if ( !canEdit( 'Control' ) ) -{ - $view = "error"; - return; +if ( !canEdit('Control') ) { + $view = 'error'; + return; } $tabs = array(); -$tabs["main"] = translate('Main'); -$tabs["move"] = translate('Move'); -$tabs["pan"] = translate('Pan'); -$tabs["tilt"] = translate('Tilt'); -$tabs["zoom"] = translate('Zoom'); -$tabs["focus"] = translate('Focus'); -$tabs["white"] = translate('White'); -$tabs["iris"] = translate('Iris'); -$tabs["presets"] = translate('Presets'); +$tabs['main'] = translate('Main'); +$tabs['move'] = translate('Move'); +$tabs['pan'] = translate('Pan'); +$tabs['tilt'] = translate('Tilt'); +$tabs['zoom'] = translate('Zoom'); +$tabs['focus'] = translate('Focus'); +$tabs['gain'] = translate('Gain'); +$tabs['white'] = translate('White'); +$tabs['iris'] = translate('Iris'); +$tabs['presets'] = translate('Presets'); -if ( isset($_REQUEST['tab']) ) - $tab = validHtmlStr($_REQUEST['tab']); -else - $tab = "main"; +$tab = isset($_REQUEST['tab']) ? validHtmlStr($_REQUEST['tab']) :'main'; -if ( isset( $_REQUEST['newControl'] ) ) -{ +if ( isset($_REQUEST['newControl']) ) { $newControl = $_REQUEST['newControl']; -} -else -{ - if ( !empty($_REQUEST['cid']) ) - { - $control = dbFetchOne( 'SELECT * FROM Controls WHERE Id = ?', NULL, array($_REQUEST['cid'] ) ); - } - else - { +} else { + if ( !empty($_REQUEST['cid']) ) { + $control = dbFetchOne('SELECT * FROM Controls WHERE Id = ?', NULL, array($_REQUEST['cid'])); + } else { $control = array( 'Name' => translate('New'), 'Type' => "Local", @@ -166,16 +157,12 @@ xhtmlHeaders(__FILE__, translate('ControlCap')." - ".$newControl['Name'] );
      $value ) -{ - if ( $tab == $name ) - { +foreach ( $tabs as $name=>$value ) { + if ( $tab == $name ) { ?>
    • $value ) @@ -201,8 +187,7 @@ if ( $tab != 'main' ) @@ -212,8 +197,7 @@ if ( $tab != 'move' ) @@ -227,8 +211,7 @@ if ( $tab != 'pan' ) @@ -242,8 +225,7 @@ if ( $tab != 'tilt' ) @@ -258,8 +240,7 @@ if ( $tab != 'zoom' ) @@ -275,8 +256,7 @@ if ( $tab != 'focus' ) @@ -292,8 +272,7 @@ if ( $tab != 'iris' ) @@ -309,8 +288,7 @@ if ( $tab != 'gain' ) @@ -326,8 +304,7 @@ if ( $tab != 'white' ) @@ -336,19 +313,17 @@ if ( $tab != 'presets' ) - +
      - -translate('Local'), 'Remote'=>translate('Remote'), 'Ffmpeg'=>translate('Ffmpeg'), 'Libvlc'=>translate('Libvlc'), 'cURL'=>"cURL"); -?> - + + @@ -373,15 +348,15 @@ switch ( $tab ) { ?> - - - - + + + + - - + + - + - - - - + + + + - - + + - + - - - - + + + + - - + + - - - - + + + + - - + + - - - - + + + + - - + + - - - - + + + + - - + + - - - - + + + + - - + + - +
      translate('Local'), 'Remote'=>translate('Remote'), 'Ffmpeg'=>translate('Ffmpeg'), 'Libvlc'=>translate('Libvlc'), 'cURL'=>'cURL'); + echo buildSelect('newControl[Type]', $types); ?>
      checked="checked"/>
      checked="checked"/>
      checked="checked">
      checked="checked"/>
      checked="checked"/>
      checked="checked">
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      checked="checked"/>
      - disabled="disabled"/> + +
    diff --git a/web/skins/classic/views/export.php b/web/skins/classic/views/export.php index b31afa879..c008b0f2b 100644 --- a/web/skins/classic/views/export.php +++ b/web/skins/classic/views/export.php @@ -138,7 +138,7 @@ $disk_space_total = 0; $event_count = 0; while ( $event_row = dbFetchNext($results) ) { $event = new ZM\Event($event_row); - $scale = max(reScale(SCALE_BASE, $event->DefaultScale(), ZM_WEB_DEFAULT_SCALE), SCALE_BASE); + $scale = max(reScale(SCALE_BASE, $event->Monitor()->DefaultScale(), ZM_WEB_DEFAULT_SCALE), SCALE_BASE); ?> Archived() ? ' class="archived"' : '' ?>> diff --git a/web/skins/classic/views/js/controlcap.js b/web/skins/classic/views/js/controlcap.js index c52ab3dd5..a94f14acd 100644 --- a/web/skins/classic/views/js/controlcap.js +++ b/web/skins/classic/views/js/controlcap.js @@ -3,21 +3,131 @@ function validateForm( form ) { // If "Can Move" is enabled, then the end user must also select at least one of the other check boxes (excluding Can Move Diagonally) if ( form.elements['newControl[CanMove]'].checked ) { - if ( !(form.elements['newControl[CanMoveCon]'].checked || form.elements['newControl[CanMoveRel]'].checked || form.elements['newControl[CanMoveAbs]'].checked || form.elements['newControl[CanMoveMap]'].checked) ) { - errors[errors.length] = "In addition to \"Can Move\", you also must select at least one of: \"Can Move Mapped\", \"Can Move Absolute\", \"Can Move Relative\", or \"Can Move Continuous\""; + if ( !( + form.elements['newControl[CanMoveCon]'].checked + || + form.elements['newControl[CanMoveRel]'].checked + || + form.elements['newControl[CanMoveAbs]'].checked + || + form.elements['newControl[CanMoveMap]'].checked + ) ) { + errors[errors.length] = 'In addition to "Can Move", you also must select at least one of: "Can Move Mapped", "Can Move Absolute", "Can Move Relative", or "Can Move Continuous"'; } } else { // Now lets check for the opposite condition. If any of the boxes below Can Move are checked, but Can Move is not checked then signal an error - if ( form.elements['newControl[CanMoveCon]'].checked || form.elements['newControl[CanMoveRel]'].checked || form.elements['newControl[CanMoveAbs]'].checked || form.elements['newControl[CanMoveMap]'].checked || form.elements['newControl[CanMoveDiag]'].checked ) { - errors[errors.length] = "\"Can Move\" must also be selected if any one of the movement types are sleceted"; + if ( form.elements['newControl[CanMoveCon]'].checked + || + form.elements['newControl[CanMoveRel]'].checked + || + form.elements['newControl[CanMoveAbs]'].checked + || + form.elements['newControl[CanMoveMap]'].checked + || + form.elements['newControl[CanMoveDiag]'].checked + ) { + errors[errors.length] = '"Can Move" must also be selected if any one of the movement types are selected.'; + } + } + // If "Can Zoom" is enabled, then the end user must also select at least one of the other check boxes + if ( form.elements['newControl[CanZoom]'].checked ) { + if ( !( + form.elements['newControl[CanZoomCon]'].checked + || + form.elements['newControl[CanZoomRel]'].checked + || + form.elements['newControl[CanZoomAbs]'].checked + ) ) { + errors[errors.length] = 'In addition to "Can Zoom", you also must select at least one of: "Can Zoom Absolute", "Can Zoom Relative", or "Can Zoom Continuous"'; + } + } else { + // Now lets check for the opposite condition. If any of the boxes below Can Zoom are checked, but Can Zoom is not checked then signal an error + + if ( form.elements['newControl[CanZoomCon]'].checked + || + form.elements['newControl[CanZoomRel]'].checked + || + form.elements['newControl[CanZoomAbs]'].checked + ) { + errors[errors.length] = '"Can Move" must also be selected if any one of the zoom types are selected.'; + } + } + // If "Can Zoom" is enabled, then the end user must also select at least one of the other check boxes + if ( form.elements['newControl[CanFocus]'].checked ) { + if ( !( + form.elements['newControl[CanFocusCon]'].checked + || + form.elements['newControl[CanFocusRel]'].checked + || + form.elements['newControl[CanFocusAbs]'].checked + ) ) { + errors[errors.length] = 'In addition to "Can Focus", you also must select at least one of: "Can Focus Absolute", "Can Focus Relative", or "Can Focus Continuous"'; + } + } else { + // Now lets check for the opposite condition. If any of the boxes below Can Zoom are checked, but Can Zoom is not checked then signal an error + + if ( form.elements['newControl[CanFocusCon]'].checked + || + form.elements['newControl[CanFocusRel]'].checked + || + form.elements['newControl[CanFocusAbs]'].checked + ) { + errors[errors.length] = '"Can Focus" must also be selected if any one of the focus types are selected.'; + } + } + // If "Can White" is enabled, then the end user must also select at least one of the other check boxes + if ( form.elements['newControl[CanWhite]'].checked ) { + if ( !( + form.elements['newControl[CanWhiteCon]'].checked + || + form.elements['newControl[CanWhiteRel]'].checked + || + form.elements['newControl[CanWhiteAbs]'].checked + ) ) { + errors[errors.length] = 'In addition to "Can White Balance", you also must select at least one of: "Can White Bal Absolute", "Can White Bal Relative", or "Can White Bal Continuous"'; + } + } else { + // Now lets check for the opposite condition. If any of the boxes below Can Zoom are checked, but Can Zoom is not checked then signal an error + + if ( form.elements['newControl[CanWhiteCon]'].checked + || + form.elements['newControl[CanWhiteRel]'].checked + || + form.elements['newControl[CanWhiteAbs]'].checked + ) { + errors[errors.length] = '"Can White Balance" must also be selected if any one of the white balance types are selected.'; + } + } + + // If "Can Iris" is enabled, then the end user must also select at least one of the other check boxes + if ( form.elements['newControl[CanIris]'].checked ) { + if ( !( + form.elements['newControl[CanIrisCon]'].checked + || + form.elements['newControl[CanIrisRel]'].checked + || + form.elements['newControl[CanIrisAbs]'].checked + ) ) { + errors[errors.length] = 'In addition to "Can Iris", you also must select at least one of: "Can Iris Absolute", "Can Iris Relative", or "Can Iris Continuous"'; + } + } else { + // Now lets check for the opposite condition. If any of the boxes below Can Zoom are checked, but Can Zoom is not checked then signal an error + + if ( form.elements['newControl[CanIrisCon]'].checked + || + form.elements['newControl[CanIrisRel]'].checked + || + form.elements['newControl[CanIrisAbs]'].checked + ) { + errors[errors.length] = '"Can Iris" must also be selected if any one of the iris types are selected.'; } } if ( errors.length ) { - alert( errors.join( "\n" ) ); - return ( false ); + alert(errors.join("\n")); + return false; } - return ( true ); + return true; } diff --git a/web/skins/classic/views/js/events.js b/web/skins/classic/views/js/events.js index ca7f7c7ec..e32281c81 100644 --- a/web/skins/classic/views/js/events.js +++ b/web/skins/classic/views/js/events.js @@ -146,10 +146,12 @@ if ( openFilterWindow ) { function thumbnail_onmouseover(event) { var img = event.target; + img.src = ''; img.src = img.getAttribute('stream_src'); } function thumbnail_onmouseout(event) { var img = event.target; + img.src = ''; img.src = img.getAttribute('still_src'); } diff --git a/web/skins/classic/views/js/export.js b/web/skins/classic/views/js/export.js index 73e81ab4a..d553f9385 100644 --- a/web/skins/classic/views/js/export.js +++ b/web/skins/classic/views/js/export.js @@ -4,7 +4,7 @@ function configureExportButton(element) { var form = element.form; var eventCount = 0; - document.querySelectorAll('input[name="eids[]"]').forEach(function(el){ + document.querySelectorAll('input[name="eids[]"]').forEach(function(el) { if ( el.checked ) { eventCount ++; } @@ -43,7 +43,6 @@ function exportProgress() { } function exportResponse(respObj, respText) { - clearInterval(exportTimer); if ( respObj.result != 'Ok' ) { $('exportProgressTicker').set('text', respObj.message); diff --git a/web/skins/classic/views/js/log.js b/web/skins/classic/views/js/log.js index b4b6b696a..811fa5f28 100644 --- a/web/skins/classic/views/js/log.js +++ b/web/skins/classic/views/js/log.js @@ -172,9 +172,9 @@ function clearLog() { logReq.cancel(); var clearParms = 'view=request&request=log&task=delete'; - var clearReq = new Request.JSON( {url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: clearResponse} ); - var tbody = $(logTable).getElement( 'tbody' ); - var rows = tbody.getElements( 'tr' ); + var clearReq = new Request.JSON({url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: clearResponse}); + var tbody = $(logTable).getElement('tbody'); + var rows = tbody.getElements('tr'); if ( rows ) { var minTime = rows[0].getElement('td').get('text'); clearParms += "&minTime="+encodeURIComponent(minTime); @@ -190,7 +190,7 @@ function filterLog() { filterFields.each( function( field ) { var selector = $('filter['+field+']'); - if ( ! selector ) { + if ( !selector ) { if ( window.console && window.console.log ) { window.console.log('No selector found for ' + field); } @@ -226,9 +226,9 @@ function exportResponse( response ) { function exportFail( request ) { $('exportLog').unspin(); - $('exportErrorText').set('text', request.status+' / '+request.statusText ); + $('exportErrorText').set('text', request.status+' / '+request.statusText); $('exportError').show(); - Error('Export request failed: '+request.status+' / '+request.statusText ); + Error('Export request failed: '+request.status+' / '+request.statusText); } function exportRequest() { @@ -237,7 +237,7 @@ function exportRequest() { $('exportError').hide(); if ( form.validate() ) { var exportParms = "view=request&request=log&task=export"; - var exportReq = new Request.JSON( {url: thisUrl, method: 'post', link: 'cancel', onSuccess: exportResponse, onFailure: exportFail} ); + var exportReq = new Request.JSON({url: thisUrl, method: 'post', link: 'cancel', onSuccess: exportResponse, onFailure: exportFail}); var selection = form.getElement('input[name=selector]:checked').get('value'); if ( selection == 'filter' || selection == 'current' ) { $$('#filters select').each( @@ -256,7 +256,7 @@ function exportRequest() { exportParms += "&maxTime="+encodeURIComponent(maxTime); } } - exportReq.send( exportParms+"&"+form.toQueryString() ); + exportReq.send(exportParms+"&"+form.toQueryString()); $('exportLog').spin(); } } @@ -265,7 +265,7 @@ function updateFilterSelectors() { Object.each(options, function( values, key ) { var selector = $('filter['+key+']'); - if ( ! selector ) { + if ( !selector ) { if ( window.console && window.console.log ) { window.console.log('No selector found for ' + key); } diff --git a/web/skins/classic/views/js/monitor.js b/web/skins/classic/views/js/monitor.js index 9d1fc7d7b..3f962ebfb 100644 --- a/web/skins/classic/views/js/monitor.js +++ b/web/skins/classic/views/js/monitor.js @@ -131,9 +131,36 @@ function initPage() { document.querySelectorAll('select[name="newMonitor[ControlId]"]').forEach(function(el) { el.onchange = window['loadLocations'].bind(el, el); }); + document.querySelectorAll('input[name="newMonitor[WebColour]"]').forEach(function(el) { + el.onchange = window['change_WebColour'].bind(el); + }); $j('.chosen').chosen(); } // end function initPage() +function change_WebColour() { + $j('#WebSwatch').css( + 'backgroundColor', + $j('input[name="newMonitor[WebColour]"]').val() + ); +} + +function getRandomColour() { + var letters = '0123456789ABCDEF'; + var colour = '#'; + for (var i = 0; i < 6; i++) { + colour += letters[Math.floor(Math.random() * 16)]; + } + return colour; +} + +function random_WebColour() { + var new_colour = getRandomColour(); + $j('input[name="newMonitor[WebColour]"]').val(new_colour); + $j('#WebSwatch').css( + 'backgroundColor', new_colour + ); +} + window.addEventListener('DOMContentLoaded', initPage); diff --git a/web/skins/classic/views/js/montage.js.php b/web/skins/classic/views/js/montage.js.php index f2dea53bf..c39d7311d 100644 --- a/web/skins/classic/views/js/montage.js.php +++ b/web/skins/classic/views/js/montage.js.php @@ -35,7 +35,7 @@ monitorData[monitorData.length] = { 'connKey': connKey() ?>, 'width': ViewWidth() ?>, 'height':ViewHeight() ?>, - 'url': 'UrlToIndex() ?>', + 'url': 'UrlToIndex( $monitor->Id() + ZM_MIN_STREAMING_PORT) ?>', 'onclick': function(){createPopup( '?view=watch&mid=Id() ?>', 'zmWatchId() ?>', 'watch', ViewWidth(), $monitor->PopupScale() ); ?>, ViewHeight(), $monitor->PopupScale() ); ?> );}, 'type': 'Type() ?>', 'refresh': 'Refresh() ?>' diff --git a/web/skins/classic/views/js/state.js b/web/skins/classic/views/js/state.js index 7165c690c..e8b4db36d 100644 --- a/web/skins/classic/views/js/state.js +++ b/web/skins/classic/views/js/state.js @@ -54,7 +54,7 @@ $j(document).ready(function() { url: thisUrl, data: formData, dataType: 'html', - enocde: true + timeout: 0 }).done(function(data) { location.reload(); }); diff --git a/web/skins/classic/views/log.php b/web/skins/classic/views/log.php index 7dd83ef2a..044a3e788 100644 --- a/web/skins/classic/views/log.php +++ b/web/skins/classic/views/log.php @@ -122,9 +122,12 @@ xhtmlHeaders(__FILE__, translate('SystemLog') );
    - - - + + + + + +
    diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 03e4ce1f8..63089dd51 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -40,8 +40,8 @@ if ( !empty($_REQUEST['mid']) ) { if ( $monitor and ZM_OPT_X10 ) $x10Monitor = dbFetchOne('SELECT * FROM TriggersX10 WHERE MonitorId = ?', NULL, array($_REQUEST['mid'])); } -if ( !$monitor ) { +if ( !$monitor ) { $nextId = getTableAutoInc('Monitors'); if ( isset($_REQUEST['dupId']) ) { $monitor = new ZM\Monitor($_REQUEST['dupId']); @@ -54,6 +54,7 @@ if ( !$monitor ) { $monitor = new ZM\Monitor(); } # end if $_REQUEST['dupID'] $monitor->Name(translate('Monitor').'-'.$nextId); + $monitor->WebColour(random_colour()); } # end if $_REQUEST['mid'] if ( ZM_OPT_X10 && empty($x10Monitor) ) { @@ -67,7 +68,6 @@ if ( ZM_OPT_X10 && empty($x10Monitor) ) { function fourcc($a, $b, $c, $d) { return ord($a) | (ord($b) << 8) | (ord($c) << 16) | (ord($d) << 24); } - if ( isset($_REQUEST['newMonitor']) ) { # Update the monitor object with whatever has been set so far. $monitor->set($_REQUEST['newMonitor']); @@ -371,13 +371,6 @@ $label_size = array( 'Large' => 2 ); -$savejpegopts = array( - 'Disabled' => 0, - 'Frames only' => 1, - 'Analysis images only (if available)' => 2, - 'Frames + Analysis images (if available)' => 3, - ); - $codecs = array( 'auto' => translate('Auto'), 'MP4' => translate('MP4'), @@ -459,8 +452,8 @@ foreach ( $tabs as $name=>$value ) { if ( $tab != 'general' ) { ?> + - GroupIds() as $group_id ) { @@ -529,6 +522,7 @@ if ( $tab != 'source' ) { } if ( $tab != 'storage' ) { ?> + @@ -612,6 +606,10 @@ switch ( $tab ) { + + + + Id()] = $Server->Name(); } echo htmlSelect( 'newMonitor[ServerId]', $servers, $monitor->ServerId() ); -?> - - - - - -'Default'); - foreach ( ZM\Storage::find(NULL, array('order'=>'lower(Name)')) as $Storage ) { - $storage_areas[$Storage->Id()] = $Storage->Name(); - } - echo htmlSelect('newMonitor[StorageId]', $storage_areas, $monitor->StorageId()); ?> @@ -921,7 +907,32 @@ if ( $monitor->Type() == 'Local' ) { } case 'storage' : ?> - + + + +'Default'); + foreach ( ZM\Storage::find(NULL, array('order'=>'lower(Name)')) as $Storage ) { + $storage_areas[$Storage->Id()] = $Storage->Name(); + } + echo htmlSelect('newMonitor[StorageId]', $storage_areas, $monitor->StorageId()); +?> + + + + + + 'Disabled', + 1 => 'Frames only', + 2 => 'Analysis images only (if available)', + 3 => 'Frames + Analysis images (if available)', + ); + echo htmlSelect('newMonitor[SaveJPEGs]', $savejpegopts, $monitor->SaveJPEGs()); +?> + + - +      - +      + sync + diff --git a/web/skins/classic/views/options.php b/web/skins/classic/views/options.php index c01209b74..2d1bb7ad2 100644 --- a/web/skins/classic/views/options.php +++ b/web/skins/classic/views/options.php @@ -110,8 +110,6 @@ foreach ( array_map('basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR)) as $ - - @@ -319,23 +317,28 @@ foreach ( array_map('basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR)) as $ } function updateSelected() { + # Turn them all off, then selectively turn the checked ones back on dbQuery('UPDATE `Users` SET `APIEnabled`=0'); - foreach ( $_REQUEST["tokenUids"] as $markUid ) { - $minTime = time(); - dbQuery('UPDATE `Users` SET `TokenMinExpiry`=? WHERE `Id`=?', array($minTime, $markUid)); + + if ( isset($_REQUEST['tokenUids']) ) { + foreach ( $_REQUEST['tokenUids'] as $markUid ) { + $minTime = time(); + dbQuery('UPDATE `Users` SET `TokenMinExpiry`=? WHERE `Id`=?', array($minTime, $markUid)); + } } - foreach ( $_REQUEST["apiUids"] as $markUid ) { - dbQuery('UPDATE `Users` SET `APIEnabled`=1 WHERE `Id`=?', array($markUid)); - + if ( isset($_REQUEST['apiUids']) ) { + foreach ( $_REQUEST['apiUids'] as $markUid ) { + dbQuery('UPDATE `Users` SET `APIEnabled`=1 WHERE `Id`=?', array($markUid)); + } } echo ''.translate('Updated').''; } - if ( array_key_exists('revokeAllTokens',$_POST) ) { + if ( array_key_exists('revokeAllTokens', $_POST) ) { revokeAllTokens(); } - if ( array_key_exists('updateSelected',$_POST) ) { + if ( array_key_exists('updateSelected', $_POST) ) { updateSelected(); } ?> diff --git a/web/skins/classic/views/state.php b/web/skins/classic/views/state.php index 12180000d..b5c069600 100644 --- a/web/skins/classic/views/state.php +++ b/web/skins/classic/views/state.php @@ -24,7 +24,11 @@ if ( !canEdit('System') ) { } ?>