diff --git a/.travis.yml b/.travis.yml index bc7b810de..da05d8970 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: cpp sudo: required -dist: xenial +dist: bionic git: depth: 9999999 notifications: @@ -37,6 +37,7 @@ env: - SMPFLAGS=-j4 OS=el DIST=8 DOCKER_REPO=knnniggett/packpack - SMPFLAGS=-j4 OS=fedora DIST=31 DOCKER_REPO=knnniggett/packpack - SMPFLAGS=-j4 OS=fedora DIST=32 DOCKER_REPO=knnniggett/packpack + - SMPFLAGS=-j4 OS=fedora DIST=33 DOCKER_REPO=knnniggett/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=focal DOCKER_REPO=iconzm/packpack diff --git a/README.md b/README.md index ab946c04c..c7bd801b7 100644 --- a/README.md +++ b/README.md @@ -70,18 +70,19 @@ Docker is a system to run applications inside isolated containers. ZoneMinder, a Dockerfile contained in this repository. However, there is still work needed to ensure that the main ZM features work properly and are documented. -## Contribution Model and Development +## Contribution Model and Development * Source hosted at [GitHub](https://github.com/ZoneMinder/ZoneMinder/) -* Report issues/questions/feature requests on [GitHub Issues](https://github.com/ZoneMinder/ZoneMinder/issues) +* Report issues at [GitHub Issues](https://github.com/ZoneMinder/ZoneMinder/issues) +* Questions/feature requests in [Slack](https://zoneminder-chat.slack.com/) or [forums](https://forums.zoneminder.com) Pull requests are very welcome! If you would like to contribute, please follow the following steps. While step 3 is optional, it is preferred. 1. Fork the repo 2. Open an issue at our [GitHub Issues Tracker](https://github.com/ZoneMinder/ZoneMinder/issues). - Describe the bug that you've found, or the feature which you're asking for. - Jot down the issue number (e.g. 456) + Follow the issue template to describe the bug or security issue you found. Please note feature + requests or questions should be posted in our user forum or Slack channel. 3. Create your feature branch (`git checkout -b 456-my-new-feature`) 4. Commit your changes (`git commit -am 'Added some feature'`) It is preferred that you 'commit early and often' instead of bunching all diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index af6a17182..cd4622df1 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -536,6 +536,8 @@ CREATE TABLE `Monitors` ( `ArchivedEventDiskSpace` bigint default NULL, `ZoneCount` TINYINT NOT NULL DEFAULT 0, `Refresh` int(10) unsigned default NULL, + `Latitude` DECIMAL(10,8), + `Longitude` DECIMAL(10,8), PRIMARY KEY (`Id`) ) ENGINE=@ZM_MYSQL_ENGINE@; diff --git a/db/zm_update-1.31.13.sql b/db/zm_update-1.31.13.sql index dd63b347a..150264027 100644 --- a/db/zm_update-1.31.13.sql +++ b/db/zm_update-1.31.13.sql @@ -13,6 +13,8 @@ SET @s = (SELECT IF( PREPARE stmt FROM @s; EXECUTE stmt; +UPDATE `Events` SET `SaveJPEGs`=(SELECT `SaveJPEGs` FROM `Monitors` WHERE Monitors.Id = MonitorId) WHERE `SaveJPEGs` IS NULL; + SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() AND table_name = 'Storage' diff --git a/db/zm_update-1.35.7.sql b/db/zm_update-1.35.7.sql new file mode 100644 index 000000000..84c3dbb1e --- /dev/null +++ b/db/zm_update-1.35.7.sql @@ -0,0 +1,23 @@ +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'Latitude' + ) > 0, +"SELECT 'Column Latitude already exists in Monitors'", +"ALTER TABLE `Monitors` ADD `Latitude` DECIMAL(10,8) AFTER `Refresh`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'Longitude' + ) > 0, +"SELECT 'Column Longitude already exists in Monitors'", +"ALTER TABLE `Monitors` ADD `Longitude` DECIMAL(10,8) AFTER `Latitude`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/distros/redhat/common/zoneminder.service.in b/distros/redhat/common/zoneminder.service.in index 8551a60e2..f4b3aad9b 100644 --- a/distros/redhat/common/zoneminder.service.in +++ b/distros/redhat/common/zoneminder.service.in @@ -5,6 +5,7 @@ Description=ZoneMinder CCTV recording and security system After=network.target mariadb.service Requires=mariadb.service +BindsTo=mariadb.service [Service] Type=forking diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 9e876324e..b594c2de2 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -28,7 +28,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.35.6 +Version: 1.35.7 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons @@ -206,8 +206,6 @@ mv -f CakePHP-Enum-Behavior-%{ceb_version} ./web/api/app/Plugin/CakePHP-Enum-Beh ./utils/zmeditconfigdata.sh ZM_OPT_CAMBOZOLA yes ./utils/zmeditconfigdata.sh ZM_OPT_CONTROL yes ./utils/zmeditconfigdata.sh ZM_CHECK_FOR_UPDATES no -./utils/zmeditconfigdata.sh ZM_DYN_SHOW_DONATE_REMINDER no -./utils/zmeditconfigdata.sh ZM_OPT_FAST_DELETE no %build # Disable LTO due to top level asm diff --git a/distros/ubuntu2004/zoneminder.init b/distros/ubuntu2004/zoneminder.init new file mode 100644 index 000000000..6132481f3 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.init @@ -0,0 +1,91 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: zoneminder +# Required-Start: $network $remote_fs $syslog +# Required-Stop: $network $remote_fs $syslog +# Should-Start: mysql +# Should-Stop: mysql +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Control ZoneMinder as a Service +# Description: ZoneMinder CCTV recording and surveillance system +### END INIT INFO +# chkconfig: 2345 20 20 + +# Source function library. +. /lib/lsb/init-functions + +prog=ZoneMinder +ZM_PATH_BIN="/usr/bin" +RUNDIR="/run/zm" +TMPDIR="/tmp/zm" +command="$ZM_PATH_BIN/zmpkg.pl" + +start() { + echo -n "Starting $prog: " + export TZ=:/etc/localtime + mkdir -p "$RUNDIR" && chown www-data:www-data "$RUNDIR" + mkdir -p "$TMPDIR" && chown www-data:www-data "$TMPDIR" + $command start + RETVAL=$? + [ $RETVAL = 0 ] && echo success + [ $RETVAL != 0 ] && echo failure + echo + [ $RETVAL = 0 ] && touch /var/lock/zm + return $RETVAL +} +stop() { + echo -n "Stopping $prog: " + # + # Why is this status check being done? + # as $command stop returns 1 if zoneminder + # is stopped, which will result in + # this returning 1, which will stuff + # dpkg when it tries to stop zoneminder before + # uninstalling . . . + # + result=`$command status` + if [ ! "$result" = "running" ]; then + echo "Zoneminder already stopped" + echo + RETVAL=0 + else + $command stop + RETVAL=$? + [ $RETVAL = 0 ] && echo success + [ $RETVAL != 0 ] && echo failure + echo + [ $RETVAL = 0 ] && rm -f /var/lock/zm + fi +} +status() { + result=`$command status` + if [ "$result" = "running" ]; then + echo "ZoneMinder is running" + RETVAL=0 + else + echo "ZoneMinder is stopped" + RETVAL=1 + fi +} + +case "$1" in +'start') + start + ;; +'stop') + stop + ;; +'restart' | 'force-reload') + stop + start + ;; +'status') + status + ;; +*) + echo "Usage: $0 { start | stop | restart | status }" + RETVAL=1 + ;; +esac +exit $RETVAL diff --git a/distros/ubuntu2004/zoneminder.postinst b/distros/ubuntu2004/zoneminder.postinst index 5d5ddd6ff..39a2fa1b0 100644 --- a/distros/ubuntu2004/zoneminder.postinst +++ b/distros/ubuntu2004/zoneminder.postinst @@ -2,13 +2,47 @@ set +e +create_db () { + echo "Checking for db" + mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload + # test if database if already present... + if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then + echo "Creating zm db" + cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf + if [ $? -ne 0 ]; then + echo "Error creating db." + exit 1; + fi + else + echo "Db exists." + fi + USER_EXISTS="$(mysql --defaults-file=/etc/mysql/debian.cnf -sse "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$ZM_DB_USER')")" + if [ $USER_EXISTS -ne 1 ]; then + echo "Creating zm user $ZM_DB_USER" + # This creates the user. + echo "CREATE USER '${ZM_DB_USER}'@${ZM_DB_HOST} IDENTIFIED BY '${ZM_DB_PASS}';" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + fi +} + +update_db () { + echo "Updating permissions" + echo "GRANT LOCK tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine,trigger,execute ON ${ZM_DB_NAME}.* TO '${ZM_DB_USER}'@${ZM_DB_HOST};" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + + zmupdate.pl --nointeractive + zmupdate.pl --nointeractive -f + + # Add any new PTZ control configurations to the database (will not overwrite) + zmcamtool.pl --import >/dev/null 2>&1 + echo "Done Updating" +} + if [ "$1" = "configure" ]; then - + . /etc/zm/zm.conf for CONFFILE in /etc/zm/conf.d/*.conf; do . "$CONFFILE" done - + # The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group chown www-data:root /var/log/zm chown www-data:www-data /var/lib/zm @@ -20,16 +54,24 @@ if [ "$1" = "configure" ]; then a2enmod cgi fi + SYSTEMD=0 + if [ -e "/run/systemd/system" ]; then + SYSTEMD=1 + echo "detected systemd" + # Ensure zoneminder is stopped + deb-systemd-invoke stop zoneminder.service || exit $? + else + # Ensure zoneminder is stopped + invoke-rc.d zoneminder stop || true + fi + if [ "$ZM_DB_HOST" = "localhost" ]; then - if [ -e "/lib/systemd/system/mysql.service" ] || [ -e "/lib/systemd/system/mariadb.service" ] || [ -e "/etc/init.d/mysql" ]; then - # Ensure zoneminder is stopped - deb-systemd-invoke stop zoneminder.service || exit $? - + if [ $SYSTEMD -eq 1 ] && ([ -e "/lib/systemd/system/mysql.service" ] || [ -e "/lib/systemd/system/mariadb.service" ]); then # # Get mysql started if it isn't running # - + if [ -e "/lib/systemd/system/mariadb.service" ]; then DBSERVICE="mariadb.service" else @@ -42,48 +84,48 @@ if [ "$1" = "configure" ]; then echo "run sudo systemctl restart $DBSERVICE then run sudo dpkg-reconfigure zoneminder." exit 1 fi - + if ! systemctl is-active --quiet mysql.service mariadb.service; then # Due to /etc/init.d service autogeneration, mysql.service always returns the status of mariadb.service # However, mariadb.service will not return the status of mysql.service. deb-systemd-invoke start $DBSERVICE fi - + # Make sure systemctl status exit code is 0; i.e. the DB is running if systemctl is-active --quiet "$DBSERVICE"; then - mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload - # test if database if already present... - if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then - echo "Creating zm db" - cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf - if [ $? -ne 0 ]; then - echo "Error creating db." - exit 1; - fi - # This creates the user. - echo "CREATE USER '${ZM_DB_USER}'@${ZM_DB_HOST} IDENTIFIED BY '${ZM_DB_PASS}';" | mysql --defaults-file=/etc/mysql/debian.cnf mysql - fi - echo "Updating permissions" - echo "GRANT LOCK tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine,trigger,execute ON ${ZM_DB_NAME}.* TO '${ZM_DB_USER}'@${ZM_DB_HOST};" | mysql --defaults-file=/etc/mysql/debian.cnf mysql - - zmupdate.pl --nointeractive - zmupdate.pl --nointeractive -f - - # Add any new PTZ control configurations to the database (will not overwrite) - zmcamtool.pl --import >/dev/null 2>&1 - echo "Done Updating; starting ZoneMinder." + create_db + update_db else echo 'NOTE: MySQL/MariaDB not running; please start mysql and run dpkg-reconfigure zoneminder when it is running.' fi + + elif [ -e "/etc/init.d/mysql" ]; then + # + # Get mysql started if it isn't + # + if ! $(/etc/init.d/mysql status >/dev/null 2>&1); then + service mysql start + fi + if $(/etc/init.d/mysql status >/dev/null 2>&1); then + create_db + update_db + else + echo 'NOTE: MySQL/MariaDB not running; please start mysql and run dpkg-reconfigure zoneminder when it is running.' + fi + else echo 'MySQL/MariaDB not found; assuming remote server.' fi - - else - echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)." fi - deb-systemd-invoke restart zoneminder.service +else + echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)." +fi + +if [ $SYSTEMD -eq 1 ]; then + deb-systemd-invoke restart zoneminder.service +#else + #service zoneminder start || true fi #DEBHELPER# diff --git a/docs/userguide/gettingstarted.rst b/docs/userguide/gettingstarted.rst index 0b40d7fb9..99f2ea4ba 100644 --- a/docs/userguide/gettingstarted.rst +++ b/docs/userguide/gettingstarted.rst @@ -51,7 +51,7 @@ This screen is called the "console" screen in ZoneMinder and shows a summary of * **A**: The options menu lets you configure many aspects of ZoneMinder. Refer to :doc:`options`. * **B**: This brings up a color coded log window that shows various system and component level logs. This window is useful if you are trying to diagnose issues. Refer to :doc:`logging`. -* **C**: ZoneMinder allows you to group monitors gor logical separation. This option lets you create new groups, associate monitors to them and edit/delete existing groups. +* **C**: ZoneMinder allows you to group monitors for logical separation. This option lets you create new groups, associate monitors to them and edit/delete existing groups. * **D**: Filters are a powerful mechanism to perform actions when certain conditions are met. ZoneMinder comes with some preset filters that keep a tab of disk space and others. Many users create their own filters for more advanced actions like sending emails when certain events occur and more. Refer to :doc:`filterevents`. * **E**: The Cycle option allows you to rotate between live views of each cofigured monitor. * **F**: The Montage option shows a collage of your monitors. You can customize them including moving them around. diff --git a/onvif/modules/MYMETA.json b/onvif/modules/MYMETA.json deleted file mode 100644 index cab2ae939..000000000 --- a/onvif/modules/MYMETA.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "abstract" : "unknown", - "author" : [ - "Jan Hochstein" - ], - "dynamic_config" : 0, - "generated_by" : "ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150010", - "license" : [ - "unknown" - ], - "meta-spec" : { - "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", - "version" : 2 - }, - "name" : "ONVIF", - "no_index" : { - "directory" : [ - "t", - "inc" - ] - }, - "prereqs" : { - "build" : { - "requires" : { - "ExtUtils::MakeMaker" : "0" - } - }, - "configure" : { - "requires" : { - "ExtUtils::MakeMaker" : "0" - } - }, - "runtime" : { - "requires" : {} - } - }, - "release_status" : "stable", - "version" : "", - "x_serialization_backend" : "JSON::PP version 4.02" -} diff --git a/onvif/modules/MYMETA.yml b/onvif/modules/MYMETA.yml deleted file mode 100644 index ea47256fc..000000000 --- a/onvif/modules/MYMETA.yml +++ /dev/null @@ -1,22 +0,0 @@ ---- -abstract: unknown -author: - - 'Jan Hochstein' -build_requires: - ExtUtils::MakeMaker: '0' -configure_requires: - ExtUtils::MakeMaker: '0' -dynamic_config: 0 -generated_by: 'ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150010' -license: unknown -meta-spec: - url: http://module-build.sourceforge.net/META-spec-v1.4.html - version: '1.4' -name: ONVIF -no_index: - directory: - - t - - inc -requires: {} -version: '' -x_serialization_backend: 'CPAN::Meta::YAML version 0.018' diff --git a/onvif/modules/pm_to_blib b/onvif/modules/pm_to_blib deleted file mode 100644 index e69de29bb..000000000 diff --git a/onvif/proxy/MYMETA.json b/onvif/proxy/MYMETA.json deleted file mode 100644 index cab2ae939..000000000 --- a/onvif/proxy/MYMETA.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "abstract" : "unknown", - "author" : [ - "Jan Hochstein" - ], - "dynamic_config" : 0, - "generated_by" : "ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150010", - "license" : [ - "unknown" - ], - "meta-spec" : { - "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", - "version" : 2 - }, - "name" : "ONVIF", - "no_index" : { - "directory" : [ - "t", - "inc" - ] - }, - "prereqs" : { - "build" : { - "requires" : { - "ExtUtils::MakeMaker" : "0" - } - }, - "configure" : { - "requires" : { - "ExtUtils::MakeMaker" : "0" - } - }, - "runtime" : { - "requires" : {} - } - }, - "release_status" : "stable", - "version" : "", - "x_serialization_backend" : "JSON::PP version 4.02" -} diff --git a/onvif/proxy/MYMETA.yml b/onvif/proxy/MYMETA.yml deleted file mode 100644 index ea47256fc..000000000 --- a/onvif/proxy/MYMETA.yml +++ /dev/null @@ -1,22 +0,0 @@ ---- -abstract: unknown -author: - - 'Jan Hochstein' -build_requires: - ExtUtils::MakeMaker: '0' -configure_requires: - ExtUtils::MakeMaker: '0' -dynamic_config: 0 -generated_by: 'ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150010' -license: unknown -meta-spec: - url: http://module-build.sourceforge.net/META-spec-v1.4.html - version: '1.4' -name: ONVIF -no_index: - directory: - - t - - inc -requires: {} -version: '' -x_serialization_backend: 'CPAN::Meta::YAML version 0.018' diff --git a/onvif/proxy/pm_to_blib b/onvif/proxy/pm_to_blib deleted file mode 100644 index e69de29bb..000000000 diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in index 9b91ff4b9..43fbcb457 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in @@ -479,6 +479,33 @@ our @options = ( type => $types{string}, category => 'system', }, + { + name => 'ZM_OPT_USE_GEOLOCATION', + description => 'Add geolocation features to ZoneMinder.', + help => 'Whether or not to enable Latitude/Longitude settings on Monitors and enable mapping options.', + type => $types{boolean}, + category => 'system', + }, + { + name => 'ZM_OPT_GEOLOCATION_TILE_PROVIDER', + description => 'Tile provider to use for maps.', + help => 'OpenStreetMaps does not itself provide the images to use in the map. There are many to choose from. Mapbox.com is one example that offers free tiles and has been tested during development of this feature.', + requires => [ + {name=>'ZM_OPT_USE_GEOLOCATION', value=>'yes'} + ], + type => $types{string}, + category => 'system', + }, + { + name => 'ZM_OPT_GEOLOCATION_ACCESS_TOKEN', + description => 'Access Token for the tile provider used for maps.', + help => 'OpenStreetMaps does not itself provide the images to use in the map. There are many to choose from. Mapbox.com is one example that offers free tiles and has been tested during development of this feature. You must go to mapbox.com and sign up and get an access token and cutnpaste it here.', + requires => [ + {name=>'ZM_OPT_USE_GEOLOCATION', value=>'yes'} + ], + type => $types{string}, + category => 'system', + }, { name => 'ZM_SYSTEM_SHUTDOWN', default => 'true', @@ -2621,7 +2648,7 @@ our @options = ( }, { name => 'ZM_WEB_EVENT_SORT_FIELD', - default => 'DateTime', + default => 'StartDateTime', description => 'Default field the event lists are sorted by', help => q` Events in lists can be initially ordered in any way you want. @@ -2633,7 +2660,7 @@ our @options = ( `, type => { db_type =>'string', - hint =>'Id|Name|Cause|MonitorName|DateTime|Length|Frames|AlarmFrames|TotScore|AvgScore|MaxScore', + hint =>'Id|Name|Cause|DiskSpace|MonitorName|StartDateTime|Length|Frames|AlarmFrames|TotScore|AvgScore|MaxScore', pattern =>qr|.|, format =>q( $1 ) }, diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Database.pm b/scripts/ZoneMinder/lib/ZoneMinder/Database.pm index 827936b86..3fdcab5e2 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Database.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Database.pm @@ -146,15 +146,15 @@ sub zmDbGetMonitors { if ( $function ) { if ( $function == DB_MON_CAPT ) { - $sql .= " where `Function` >= 'Monitor'"; + $sql .= " WHERE `Function` >= 'Monitor'"; } elsif ( $function == DB_MON_ACTIVE ) { - $sql .= " where `Function` > 'Monitor'"; + $sql .= " WHERE `Function` > 'Monitor'"; } elsif ( $function == DB_MON_MOTION ) { - $sql .= " where `Function` = 'Modect' or Function = 'Mocord'"; + $sql .= " WHERE `Function` = 'Modect' OR `Function` = 'Mocord'"; } elsif ( $function == DB_MON_RECORD ) { - $sql .= " where `Function` = 'Record' or Function = 'Mocord'"; + $sql .= " WHERE `Function` = 'Record' OR `Function` = 'Mocord'"; } elsif ( $function == DB_MON_PASSIVE ) { - $sql .= " where `Function` = 'Nodect'"; + $sql .= " WHERE `Function` = 'Nodect'"; } } my $sth = $dbh->prepare_cached( $sql ); diff --git a/scripts/zmcontrol.pl.in b/scripts/zmcontrol.pl.in index 74886b4d4..c9a548eef 100644 --- a/scripts/zmcontrol.pl.in +++ b/scripts/zmcontrol.pl.in @@ -83,7 +83,7 @@ if ( $options{command} ) { my $server_up; while ( $tries and ! ( $server_up = connect(CLIENT, $saddr) ) ) { Debug("Failed to connect to zmcontrol server at $sock_file"); - runCommand("zmdc.pl start zmcontrol.pl --id=$id"); + runCommand("zmdc.pl start zmcontrol.pl --id $id"); sleep 1; $tries -= 1; } @@ -142,9 +142,9 @@ if ( $options{command} ) { .strftime('%y/%m/%d %H:%M:%S', localtime()) ); - $0 = $0." --id=$id"; + $0 = $0.' --id '.$id; - my $control = "ZoneMinder::Control::$protocol"->new($id); + my $control = ('ZoneMinder::Control::'.$protocol)->new($id); my $control_key = $control->getKey(); $control->loadMonitor(); diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in index 5b478316d..5ff316078 100644 --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -691,6 +691,7 @@ sub substituteTags { $text =~ s/%EC%/$Event->{Cause}/g; $text =~ s/%ED%/$Event->{Notes}/g; $text =~ s/%ET%/$Event->{StartTime}/g; + $text =~ s/%EVF%/$$Event{DefaultVideo}/g; # Event video filename $text =~ s/%EL%/$Event->{Length}/g; $text =~ s/%EF%/$Event->{Frames}/g; $text =~ s/%EFA%/$Event->{AlarmFrames}/g; @@ -1017,8 +1018,7 @@ sub executeCommand { my $event_path = $Event->Path(); - my $command = $filter->{AutoExecuteCmd}; - $command .= " $event_path"; + my $command = $filter->{AutoExecuteCmd}.' '.$event_path; $command = substituteTags($command, $filter, $Event); Info("Executing '$command'"); diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 76156c3a5..6ab53cf54 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -280,8 +280,8 @@ Event::~Event() { // Should not be static because we might be multi-threaded char sql[ZM_SQL_LGE_BUFSIZ]; - snprintf(sql, sizeof(sql), - "UPDATE Events SET Name='%s%" PRIu64 "', EndTime = from_unixtime(%ld), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64, + snprintf(sql, sizeof(sql), + "UPDATE Events SET Name='%s%" PRIu64 "', EndTime = from_unixtime(%ld), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64 " AND Name='New Event'", monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, @@ -294,6 +294,22 @@ Event::~Event() { sleep(1); db_mutex.lock(); } + if ( !mysql_affected_rows(&dbconn) ) { + // Name might have been changed during recording, so just do the update without changing the name. + snprintf(sql, sizeof(sql), + "UPDATE Events SET EndTime = from_unixtime(%ld), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64, + end_time.tv_sec, + delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, + frames, alarm_frames, + tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, + id); + while ( mysql_query(&dbconn, sql) && !zm_terminate ) { + db_mutex.unlock(); + Error("Can't update event: %s reason: %s", sql, mysql_error(&dbconn)); + sleep(1); + db_mutex.lock(); + } + } // end if no changed rows due to Name change during recording db_mutex.unlock(); } // Event::~Event() diff --git a/src/zm_monitor.h b/src/zm_monitor.h index e020ba391..f1db76195 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -88,7 +88,7 @@ public: } Orientation; typedef enum { - UNKNOWN, + UNKNOWN=-1, IDLE, PREALARM, ALARM, diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index 9bb8d26e5..974409ff3 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -247,7 +247,7 @@ bool Zone::CheckAlarms(const Image *delta_image) { if ( config.record_diag_images_fifo ) { FifoDebug(5, "{\"zone\":%d,\"type\":\"ALRM\",\"pixels\":%d,\"avg_diff\":%d}", - id,alarm_pixels, pixel_diff); + id, alarm_pixels, pixel_diff); } if ( alarm_pixels ) { @@ -264,11 +264,7 @@ bool Zone::CheckAlarms(const Image *delta_image) { return false; } - if ( max_alarm_pixels != 0 ) - score = (100*alarm_pixels)/max_alarm_pixels; - else - score = (100*alarm_pixels)/polygon.Area(); - + score = (100*alarm_pixels)/(max_alarm_pixels?max_alarm_pixels:polygon.Area()); if ( score < 1 ) score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ Debug(5, "Current score is %d", score); @@ -358,8 +354,7 @@ bool Zone::CheckAlarms(const Image *delta_image) { if ( check_method >= BLOBS ) { Debug(5, "Checking for blob pixels"); - typedef struct { unsigned char tag; int count; int lo_x; int hi_x; int lo_y; int hi_y; } BlobStats; - BlobStats blob_stats[256]; + // ICON FIXME Would like to get rid of this memset memset(blob_stats, 0, sizeof(BlobStats)*256); uint8_t *spdiff; uint8_t last_x, last_y; @@ -369,22 +364,15 @@ bool Zone::CheckAlarms(const Image *delta_image) { int lo_x = ranges[y].lo_x; int hi_x = ranges[y].hi_x; - pdiff = (uint8_t*)diff_image->Buffer( lo_x, y ); + pdiff = (uint8_t*)diff_image->Buffer(lo_x, y); for ( int x = lo_x; x <= hi_x; x++, pdiff++ ) { if ( *pdiff == WHITE ) { Debug(9, "Got white pixel at %d,%d (%p)", x, y, pdiff); - //last_x = (x>lo_x)?*(pdiff-1):0; - //last_y = (y>lo_y&&x>=last_lo_x&&x<=last_hi_x)?*(pdiff-diff_width):0; - last_x = 0; - if ( x > 0 ) { - if ( (x-1) >= lo_x ) { - last_x = *(pdiff-1); - } - } + last_x = ((x > 0) && ( (x-1) >= lo_x )) ? *(pdiff-1) : 0; last_y = 0; - if (y > 0 ) { + if ( y > 0 ) { if ( (y-1) >= lo_y && ranges[(y-1)].lo_x <= x && ranges[(y-1)].hi_x >= x ) { last_y = *(pdiff-diff_width); } @@ -631,7 +619,7 @@ bool Zone::CheckAlarms(const Image *delta_image) { } if ( max_blob_pixels != 0 ) - score = (100*alarm_blob_pixels)/(max_blob_pixels); + score = (100*alarm_blob_pixels)/max_blob_pixels; else score = (100*alarm_blob_pixels)/polygon.Area(); @@ -758,67 +746,42 @@ bool Zone::CheckAlarms(const Image *delta_image) { } bool Zone::ParsePolygonString(const char *poly_string, Polygon &polygon) { - Debug(3, "Parsing polygon string '%s'", poly_string); - - char *str_ptr = new char[strlen(poly_string)+1]; - char *str = str_ptr; - strcpy(str, poly_string); + char *str = (char *)poly_string; char *ws; + char *cp; int n_coords = 0; int max_n_coords = strlen(str)/4; Coord *coords = new Coord[max_n_coords]; - while( true ) { - if ( *str == '\0' ) { - break; - } - ws = strchr(str, ' '); - if ( ws ) { - *ws = '\0'; - } - char *cp = strchr(str, ','); + while ( *str != '\0' ) { + cp = strchr(str, ','); if ( !cp ) { Error("Bogus coordinate %s found in polygon string", str); - delete[] coords; - delete[] str_ptr; - return false; - } else { - *cp = '\0'; - char *xp = str; - char *yp = cp+1; + break; + } + int x = atoi(str); + int y = atoi(cp+1); + Debug(3, "Got coordinate %d,%d from polygon string", x, y); + coords[n_coords++] = Coord(x, y); - int x = atoi(xp); - int y = atoi(yp); - - Debug(3, "Got coordinate %d,%d from polygon string", x, y); -#if 0 - if ( x < 0 ) - x = 0; - else if ( x >= width ) - x = width-1; - if ( y < 0 ) - y = 0; - else if ( y >= height ) - y = height-1; -#endif - coords[n_coords++] = Coord( x, y ); - } + ws = strchr(cp+2, ' '); if ( ws ) str = ws+1; else break; - } - polygon = Polygon(n_coords, coords); + } // end while ! end of string - Debug(3, "Successfully parsed polygon string"); - //printf( "Area: %d\n", pg.Area() ); - //printf( "Centre: %d,%d\n", pg.Centre().X(), pg.Centre().Y() ); + if ( n_coords > 2 ) { + Debug(3, "Successfully parsed polygon string %s", str); + polygon = Polygon(n_coords, coords); + } else { + Error("Not enough coordinates to form a polygon!"); + n_coords = 0; + } delete[] coords; - delete[] str_ptr; - - return true; -} + return n_coords ? true : false; +} // end bool Zone::ParsePolygonString(const char *poly_string, Polygon &polygon) bool Zone::ParseZoneString(const char *zone_string, int &zone_id, int &colour, Polygon &polygon) { Debug(3, "Parsing zone string '%s'", zone_string); @@ -827,13 +790,12 @@ bool Zone::ParseZoneString(const char *zone_string, int &zone_id, int &colour, P char *str = str_ptr; strcpy(str, zone_string); + zone_id = strtol(str, 0, 10); + Debug(3, "Got zone %d from zone string", zone_id); + char *ws = strchr(str, ' '); if ( !ws ) { Debug(3, "No initial whitespace found in zone string '%s', finishing", str); - } - zone_id = strtol(str, 0, 10); - Debug(3, "Got zone %d from zone string", zone_id); - if ( !ws ) { delete[] str_ptr; return true; } @@ -841,13 +803,11 @@ bool Zone::ParseZoneString(const char *zone_string, int &zone_id, int &colour, P *ws = '\0'; str = ws+1; + colour = strtol(str, 0, 16); + Debug(3, "Got colour %06x from zone string", colour); ws = strchr(str, ' '); if ( !ws ) { Debug(3, "No secondary whitespace found in zone string '%s', finishing", zone_string); - } - colour = strtol(str, 0, 16); - Debug(3, "Got colour %06x from zone string", colour); - if ( !ws ) { delete[] str_ptr; return true; } @@ -855,27 +815,28 @@ bool Zone::ParseZoneString(const char *zone_string, int &zone_id, int &colour, P str = ws+1; bool result = ParsePolygonString(str, polygon); - - //printf( "Area: %d\n", pg.Area() ); - //printf( "Centre: %d,%d\n", pg.Centre().X(), pg.Centre().Y() ); - delete[] str_ptr; return result; -} +} // end bool Zone::ParseZoneString(const char *zone_string, int &zone_id, int &colour, Polygon &polygon) int Zone::Load(Monitor *monitor, Zone **&zones) { static char sql[ZM_SQL_MED_BUFSIZ]; db_mutex.lock(); - snprintf(sql, sizeof(sql), "select Id,Name,Type+0,Units,Coords,AlarmRGB,CheckMethod+0,MinPixelThreshold,MaxPixelThreshold,MinAlarmPixels,MaxAlarmPixels,FilterX,FilterY,MinFilterPixels,MaxFilterPixels,MinBlobPixels,MaxBlobPixels,MinBlobs,MaxBlobs,OverloadFrames,ExtendAlarmFrames from Zones where MonitorId = %d order by Type, Id", monitor->Id()); + snprintf(sql, sizeof(sql), "SELECT Id,Name,Type+0,Units,Coords,AlarmRGB,CheckMethod+0," + "MinPixelThreshold,MaxPixelThreshold,MinAlarmPixels,MaxAlarmPixels," + "FilterX,FilterY,MinFilterPixels,MaxFilterPixels," + "MinBlobPixels,MaxBlobPixels,MinBlobs,MaxBlobs," + "OverloadFrames,ExtendAlarmFrames" + " FROM Zones WHERE MonitorId = %d ORDER BY Type, Id", monitor->Id()); if ( mysql_query(&dbconn, sql) ) { Error("Can't run query: %s", mysql_error(&dbconn)); db_mutex.unlock(); return 0; } - MYSQL_RES *result = mysql_store_result( &dbconn ); + MYSQL_RES *result = mysql_store_result(&dbconn); if ( !result ) { Error("Can't use query result: %s", mysql_error(&dbconn)); db_mutex.unlock(); @@ -944,7 +905,13 @@ int Zone::Load(Monitor *monitor, Zone **&zones) { } else if ( atoi(dbrow[2]) == Zone::PRIVACY ) { zones[i] = new Zone(monitor, Id, Name, (Zone::ZoneType)Type, polygon); } - zones[i] = new Zone(monitor, Id, Name, (Zone::ZoneType)Type, polygon, AlarmRGB, (Zone::CheckMethod)CheckMethod, MinPixelThreshold, MaxPixelThreshold, MinAlarmPixels, MaxAlarmPixels, Coord( FilterX, FilterY ), MinFilterPixels, MaxFilterPixels, MinBlobPixels, MaxBlobPixels, MinBlobs, MaxBlobs, OverloadFrames, ExtendAlarmFrames); + zones[i] = new Zone( + monitor, Id, Name, (Zone::ZoneType)Type, polygon, AlarmRGB, + (Zone::CheckMethod)CheckMethod, MinPixelThreshold, MaxPixelThreshold, + MinAlarmPixels, MaxAlarmPixels, Coord( FilterX, FilterY ), + MinFilterPixels, MaxFilterPixels, + MinBlobPixels, MaxBlobPixels, MinBlobs, MaxBlobs, + OverloadFrames, ExtendAlarmFrames); } // end foreach row mysql_free_result(result); return n_zones; @@ -953,9 +920,9 @@ int Zone::Load(Monitor *monitor, Zone **&zones) { bool Zone::DumpSettings(char *output, bool /*verbose*/) { output[0] = 0; - sprintf( output+strlen(output), " Id : %d\n", id ); - sprintf( output+strlen(output), " Label : %s\n", label ); - sprintf( output+strlen(output), " Type: %d - %s\n", type, + sprintf(output+strlen(output), " Id : %d\n", id ); + sprintf(output+strlen(output), " Label : %s\n", label ); + sprintf(output+strlen(output), " Type: %d - %s\n", type, type==ACTIVE?"Active":( type==INCLUSIVE?"Inclusive":( type==EXCLUSIVE?"Exclusive":( @@ -1020,10 +987,10 @@ void Zone::std_alarmedpixels( *pdiff = BLACK; } } - } + } // end for y = lo_y to hi_y /* Store the results */ *pixel_count = pixelsalarmed; *pixel_sum = pixelsdifference; Debug(7, "STORED pixelsalarmed(%d), pixelsdifference(%d)", pixelsalarmed, pixelsdifference); -} +} // end void Zone::std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum) diff --git a/src/zm_zone.h b/src/zm_zone.h index 0c7b26a57..7d7797725 100644 --- a/src/zm_zone.h +++ b/src/zm_zone.h @@ -32,15 +32,14 @@ class Monitor; // This describes a 'zone', or an area of an image that has certain // detection characteristics. // -class Zone -{ +class Zone { protected: - struct Range - { + struct Range { int lo_x; int hi_x; int off_x; }; + typedef struct { unsigned char tag; int count; int lo_x; int hi_x; int lo_y; int hi_y; } BlobStats; public: typedef enum { ACTIVE=1, INCLUSIVE, EXCLUSIVE, PRECLUSIVE, INACTIVE, PRIVACY } ZoneType; @@ -52,8 +51,8 @@ protected: int id; char *label; - ZoneType type; - Polygon polygon; + ZoneType type; + Polygon polygon; Rgb alarm_rgb; CheckMethod check_method; @@ -67,17 +66,18 @@ protected: int min_filter_pixels; int max_filter_pixels; + BlobStats blob_stats[256]; int min_blob_pixels; int max_blob_pixels; int min_blobs; int max_blobs; - int overload_frames; + int overload_frames; int extend_alarm_frames; // Outputs/Statistics - bool alarmed; - bool was_alarmed; + bool alarmed; + bool was_alarmed; int pixel_diff; unsigned int alarm_pixels; int alarm_filter_pixels; @@ -139,8 +139,7 @@ public: inline Coord GetAlarmCentre() const { return( alarm_centre ); } inline unsigned int Score() const { return( score ); } - inline void ResetStats() - { + inline void ResetStats() { alarmed = false; was_alarmed = false; pixel_diff = 0; diff --git a/utils/do_debian_package.sh b/utils/do_debian_package.sh index f95cc6523..d6107c5a9 100755 --- a/utils/do_debian_package.sh +++ b/utils/do_debian_package.sh @@ -156,10 +156,14 @@ if [ ! -d "${GITHUB_FORK}_zoneminder_release" ]; then if [ -d "${GITHUB_FORK}_ZoneMinder.git" ]; then echo "Using local clone ${GITHUB_FORK}_ZoneMinder.git to pull from." cd "${GITHUB_FORK}_ZoneMinder.git" - echo "git pull..." - git pull + echo "git fetch..." + git fetch echo "git checkout $BRANCH" git checkout $BRANCH + if [ $? -ne 0 ]; then + echo "Failed to switch to branch." + exit 1; + fi; echo "git pull..." git pull cd ../ @@ -335,7 +339,7 @@ EOF dput="Y"; if [ "$INTERACTIVE" != "no" ]; then - read -p "Ready to dput $SC to $PPA ? Y/N..."; + read -p "Ready to dput $SC to $PPA ? Y/n..."; if [[ "$REPLY" == [yY] ]]; then dput $PPA $SC fi; diff --git a/utils/packpack/startpackpack.sh b/utils/packpack/startpackpack.sh index 1e9f6557f..1b2eafd8f 100755 --- a/utils/packpack/startpackpack.sh +++ b/utils/packpack/startpackpack.sh @@ -361,7 +361,7 @@ elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbia execpackpack # Try to install and run the newly built zoneminder package - if [ "${OS}" == "ubuntu" ] && [ "${DIST}" == "xenial" ] && [ "${ARCH}" == "x86_64" ] && [ "${TRAVIS}" == "true" ]; then + if [ "${OS}" == "ubuntu" ] && [ "${DIST}" == "bionic" ] && [ "${ARCH}" == "x86_64" ] && [ "${TRAVIS}" == "true" ]; then echo "Begin Deb package installation..." install_deb fi diff --git a/utils/resample_event_video.sh b/utils/resample_event_video.sh new file mode 100755 index 000000000..845216cc0 --- /dev/null +++ b/utils/resample_event_video.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +FFMPEG=`which ffmpeg`; + +if [ -z "$FFMPEG" ]; then + echo "You must install the ffmpeg package. Try sudo apt-get install ffmpeg"; + exit; +fi + +for i in "$@" +do +case $i in + -r=*|--rate=*) + FPS="${i#*=}" + shift + ;; + -f=*|--file=*) + VIDEO_FILE="${i#*=}" + shift + ;; + + *) + EVENT_PATH="${i#*=}" + shift + ;; +esac +done + +if [ -z "$EVENT_PATH" ]; then + echo "You must specify the path to the event."; + exit 255 +fi; + +if [ -z "$FPS" ]; then + echo "You must specify the new fps."; + exit 255 +fi; + +$FFMPEG -i "$EVENT_PATH/$VIDEO_FILE" -filter:v fps=fps=$FPS "$EVENT_PATH/output.mp4" +echo $? +if [ $? -eq 0 ]; then + mv "$EVENT_PATH/output.mp4" "$EVENT_PATH/$VIDEO_FILE" +fi; + +exit 0; diff --git a/version b/version index 21cbe96c2..96eced874 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.35.6 +1.35.7 diff --git a/web/ajax/controlcaps.php b/web/ajax/controlcaps.php new file mode 100644 index 000000000..9e655c128 --- /dev/null +++ b/web/ajax/controlcaps.php @@ -0,0 +1,33 @@ + diff --git a/web/ajax/events.php b/web/ajax/events.php index 7b852c559..16c777ebf 100644 --- a/web/ajax/events.php +++ b/web/ajax/events.php @@ -16,10 +16,10 @@ if ( canEdit('Events') ) { switch ( $_REQUEST['action'] ) { case 'archive' : case 'unarchive' : - $archiveVal = ($_REQUEST['action'] == 'archive')?1:0; + $archiveVal = ($_REQUEST['action'] == 'archive') ? 1 : 0; dbQuery( 'UPDATE Events SET Archived = ? WHERE Id = ?', - array($archiveVal, $_REQUEST['id']) + array($archiveVal, $eid) ); break; case 'delete' : diff --git a/web/ajax/log.php b/web/ajax/log.php index d5483b7ca..16f7ac573 100644 --- a/web/ajax/log.php +++ b/web/ajax/log.php @@ -185,7 +185,7 @@ switch ( $_REQUEST['task'] ) { $Servers = ZM\Server::find(); $servers_by_Id = array(); # There is probably a better way to do this. - foreach ( $servers as $server ) { + foreach ( $Servers as $server ) { $servers_by_Id[$server->Id()] = $server; } @@ -245,6 +245,9 @@ switch ( $_REQUEST['task'] ) { } $exportKey = substr(md5(rand()), 0, 8); $exportFile = 'zm-log.'.$exportExt; + + // mkdir will generate a warning if it exists, but that is ok + error_reporting(0); if ( ! ( mkdir(ZM_DIR_EXPORTS) || file_exists(ZM_DIR_EXPORTS) ) ) { ZM\Fatal('Can\'t create exports dir at \''.ZM_DIR_EXPORTS.'\''); } @@ -371,8 +374,16 @@ switch ( $_REQUEST['task'] ) { <'.strtolower($field).'>'.htmlspecialchars($value).' ' ); fwrite( $exportFP, - ' - '.translate('DateTime').''.translate('Component').''.translate('Pid').''.translate('Level').''.translate('Message').''.translate('File').''.translate('Line').' + ' + + '.translate('DateTime').' + '.translate('Component').' + '.translate('Server').' + '.translate('Pid').' + '.translate('Level').' + '.translate('Message').' + '.translate('File').' + '.translate('Line').' ' ); diff --git a/web/ajax/modal.php b/web/ajax/modal.php new file mode 100644 index 000000000..bd96b137f --- /dev/null +++ b/web/ajax/modal.php @@ -0,0 +1,24 @@ + diff --git a/web/ajax/modals/delconfirm.php b/web/ajax/modals/delconfirm.php new file mode 100644 index 000000000..02f742cff --- /dev/null +++ b/web/ajax/modals/delconfirm.php @@ -0,0 +1,26 @@ + + + diff --git a/web/skins/classic/views/donate.php b/web/ajax/modals/donate.php similarity index 70% rename from web/skins/classic/views/donate.php rename to web/ajax/modals/donate.php index 502377133..374d4e0d2 100644 --- a/web/skins/classic/views/donate.php +++ b/web/ajax/modals/donate.php @@ -18,10 +18,7 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -if ( !canEdit('System') ) { - $view = 'error'; - return; -} +if ( !canEdit('System') ) return; $options = array( 'go' => translate('DonateYes'), @@ -45,25 +42,25 @@ $options = array( + diff --git a/web/ajax/modals/enoperm.php b/web/ajax/modals/enoperm.php new file mode 100644 index 000000000..6fd2fbe23 --- /dev/null +++ b/web/ajax/modals/enoperm.php @@ -0,0 +1,24 @@ + + + diff --git a/web/ajax/modals/eventdetail.php b/web/ajax/modals/eventdetail.php new file mode 100644 index 000000000..bb2f53ecc --- /dev/null +++ b/web/ajax/modals/eventdetail.php @@ -0,0 +1,89 @@ +'; + $newEvent = dbFetchOne('SELECT E.* FROM Events AS E WHERE E.Id = ?', NULL, array($eid)); + +} elseif ( $eids ) { // Multi Event Mode + + $title = translate('Events'); + $sql = 'SELECT E.* FROM Events AS E WHERE '; + $sqlWhere = array(); + $sqlValues = array(); + foreach ( $eids as $eid ) { + $eid = validInt($eid); + $inputs .= ''; + $sqlWhere[] = 'E.Id = ?'; + $sqlValues[] = $eid; + } + unset($eid); + $sql .= join(' OR ', $sqlWhere); + foreach( dbFetchAll( $sql, NULL, $sqlValues ) as $row ) { + if ( !isset($newEvent) ) { + $newEvent = $row; + } else { + if ( $newEvent['Cause'] && $newEvent['Cause'] != $row['Cause'] ) + $newEvent['Cause'] = ''; + if ( $newEvent['Notes'] && $newEvent['Notes'] != $row['Notes'] ) + $newEvent['Notes'] = ''; + } + } + +} else { // Event Mode not specified - should we really proceed if neither eid nor eids is set? + $title = translate('Events'); +} + +?> + + diff --git a/web/ajax/modals/filterdebug.php b/web/ajax/modals/filterdebug.php new file mode 100644 index 000000000..725539a99 --- /dev/null +++ b/web/ajax/modals/filterdebug.php @@ -0,0 +1,35 @@ + diff --git a/web/skins/classic/views/function.php b/web/ajax/modals/function.php similarity index 85% rename from web/skins/classic/views/function.php rename to web/ajax/modals/function.php index 83d67d54a..e6bab2718 100644 --- a/web/skins/classic/views/function.php +++ b/web/ajax/modals/function.php @@ -18,17 +18,18 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -if ( !canEdit('Monitors') ) { - $view = 'error'; - return; -} -?> +if ( !canEdit('Monitors') ) return; +?> diff --git a/web/ajax/modals/group.php b/web/ajax/modals/group.php new file mode 100644 index 000000000..119149951 --- /dev/null +++ b/web/ajax/modals/group.php @@ -0,0 +1,135 @@ +Id(); +} + +function get_children($Group) { + global $children; + + $kids = array(); + if ( isset( $children[$Group->Id()] ) ) { + $kids += array_map('get_Id', $children[$Group->Id()]); + foreach ( $children[$Group->Id()] as $G ) { + foreach ( get_children($G) as $id ) { + $kids[] = $id; + } + } + } + return $kids; +} + +function parentGrpSelect($newGroup) { + $Groups = array(); + foreach ( ZM\Group::find() as $Group ) { + $Groups[$Group->Id()] = $Group; + } + + # This array is indexed by parent_id + $children = array(); + + foreach ( $Groups as $id=>$Group ) { + if ( $Group->ParentId() != null ) { + if ( ! isset( $children[$Group->ParentId()] ) ) + $children[$Group->ParentId()] = array(); + $children[$Group->ParentId()][] = $Group; + } + } + + $kids = get_children($newGroup); + if ( $newGroup->Id() ) + $kids[] = $newGroup->Id(); + $sql = 'SELECT Id,Name FROM `Groups`'.(count($kids)?' WHERE Id NOT IN ('.implode(',',array_map(function(){return '?';}, $kids)).')' : '').' ORDER BY Name'; + $options = array(''=>'None'); + + foreach ( dbFetchAll($sql, null, $kids) as $option ) { + $options[$option['Id']] = str_repeat('  ', $Groups[$option['Id']]->depth()) . $option['Name']; + } + + return htmlSelect('newGroup[ParentId]', $options, $newGroup->ParentId(), array('data-on-change'=>'configModalBtns')); +} + +function monitorList($newGroup) { + $result = ''; + + $monitors = dbFetchAll('SELECT Id,Name FROM Monitors ORDER BY Sequence ASC'); + $monitorIds = $newGroup->MonitorIds(); + foreach ( $monitors as $monitor ) { + if ( visibleMonitor($monitor['Id']) ) { + $result .= ''.PHP_EOL; + } + } + + return $result; +} + +// +// INITIAL SANITY CHECKS +// + +if ( !canEdit('Groups') ) { + $view = 'error'; + return; +} + +if ( !empty($_REQUEST['gid']) ) { + $newGroup = new ZM\Group($_REQUEST['gid']); +} else { + $newGroup = new ZM\Group(); +} + +// +// BEGIN HTML +// +?> + diff --git a/web/ajax/modals/log_export.php b/web/ajax/modals/log_export.php new file mode 100644 index 000000000..8f359bb86 --- /dev/null +++ b/web/ajax/modals/log_export.php @@ -0,0 +1,46 @@ +