diff --git a/.eslintrc.js b/.eslintrc.js index 64950ee0d..0bb050ad1 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -3,6 +3,7 @@ module.exports = { "env": { "browser": true, + "es6": true, }, "extends": ["google"], "overrides": [{ diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..8804e639c --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: [connortechnology,pliablepixels] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: zoneminder # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index dbb351b80..7751de627 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,3 +1,5 @@ + **Describe Your Environment** + **If the issue concerns a camera** + **Describe the bug** + **To Reproduce** + **Expected behavior** + **Debug Logs** ``` + ``` diff --git a/.github/stale.yml b/.github/stale.yml index 94048df3d..4dde9b5a7 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,16 +1,17 @@ # Configuration for probot-stale - https://github.com/probot/stale # Number of days of inactivity before an issue becomes stale -daysUntilStale: 60 +daysUntilStale: 180 # Number of days of inactivity before a stale issue is closed -daysUntilClose: 7 +daysUntilClose: 30 # Issues with these labels will never be considered stale exemptLabels: - "Under Review" - "Bounty" - "bug" + - "Feature" # Set to true to ignore issues in a milestone (defaults to false) exemptMilestones: true diff --git a/.gitignore b/.gitignore index 0dae662d7..152802d29 100644 --- a/.gitignore +++ b/.gitignore @@ -120,7 +120,7 @@ src/CMakeFiles/ src/cmake_install.cmake src/libzm.a src/nph-zms -src/zm_config.h +src/zm_config_data.h src/zm_config_defines.h src/zma src/zmc @@ -157,3 +157,4 @@ web/undef.log zm.conf zmconfgen.pl zmlinkcontent.sh +**/.DS_Store diff --git a/.travis.yml b/.travis.yml index 80541eaab..da05d8970 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: cpp sudo: required -dist: trusty +dist: bionic git: depth: 9999999 notifications: @@ -13,7 +13,7 @@ addons: ssh_known_hosts: zmrepo.zoneminder.com apt: sources: - - sourceline: ppa:iconnor/zoneminder + - sourceline: ppa:iconnor/zoneminder-master - key_url: http://keyserver.ubuntu.com:11371/pks/lookup?op=get&search=0x4D0BF748776FFB04 packages: - gdebi @@ -32,14 +32,17 @@ install: - update-binfmts --enable qemu-arm env: - - SMPFLAGS=-j4 OS=el DIST=7 - - SMPFLAGS=-j4 OS=fedora DIST=28 DOCKER_REPO=knnniggett/packpack - - SMPFLAGS=-j4 OS=fedora DIST=29 DOCKER_REPO=knnniggett/packpack - - SMPFLAGS=-j4 OS=ubuntu DIST=trusty - - SMPFLAGS=-j4 OS=ubuntu DIST=xenial - - SMPFLAGS=-j4 OS=ubuntu DIST=trusty ARCH=i386 - - SMPFLAGS=-j4 OS=ubuntu DIST=xenial ARCH=i386 - - SMPFLAGS=-j4 OS=raspbian DIST=stretch ARCH=armhf DOCKER_REPO=knnniggett/packpack + - SMPFLAGS=-j4 OS=eslint DIST=eslint + - SMPFLAGS=-j4 OS=el DIST=7 DOCKER_REPO=knnniggett/packpack + - 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 + - SMPFLAGS=-j4 OS=debian DIST=stretch DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=debian DIST=buster DOCKER_REPO=iconzm/packpack compiler: - gcc @@ -47,12 +50,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/CMakeLists.txt b/CMakeLists.txt index 9d882942d..2079296f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,6 +65,8 @@ set(CMAKE_C_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2") set(CMAKE_CXX_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2") set(CMAKE_C_FLAGS_DEBUG "-Wall -D__STDC_CONSTANT_MACROS -g") set(CMAKE_CXX_FLAGS_DEBUG "-Wall -D__STDC_CONSTANT_MACROS -g") +set(CMAKE_C_FLAGS_OPTIMISED "-Wall -D__STDC_CONSTANT_MACROS -O3") +set(CMAKE_CXX_FLAGS_OPTIMISED "-Wall -D__STDC_CONSTANT_MACROS -O3") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") set (CMAKE_CXX_STANDARD 11) @@ -140,9 +142,9 @@ set(ZM_TMPDIR "/var/tmp/zm" CACHE PATH "Location of temporary files, default: /tmp/zm") set(ZM_LOGDIR "/var/log/zm" CACHE PATH "Location of generated log files, default: /var/log/zm") -set(ZM_WEBDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/zoneminder/www" CACHE PATH +set(ZM_WEBDIR "${CMAKE_INSTALL_FULL_DATADIR}/zoneminder/www" CACHE PATH "Location of the web files, default: /${CMAKE_INSTALL_DATADIR}/zoneminder/www") -set(ZM_CGIDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin" CACHE PATH +set(ZM_CGIDIR "${CMAKE_INSTALL_FULL_LIBEXECDIR}/zoneminder/cgi-bin" CACHE PATH "Location of the cgi-bin files, default: /${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin") set(ZM_CACHEDIR "/var/cache/zoneminder" CACHE PATH "Location of the web server cache busting files, default: /var/cache/zoneminder") @@ -189,6 +191,8 @@ set(ZM_NO_MMAP "OFF" CACHE BOOL experience problems with the shared memory. default: OFF") set(ZM_NO_LIBVLC "OFF" CACHE BOOL "Set to ON to skip libvlc checks and force building ZM without libvlc. default: OFF") +set(ZM_NO_LIBVNC "OFF" CACHE BOOL +"Set to ON to skip libvnc checks and force building ZM without libvnc. default: OFF") set(ZM_NO_CURL "OFF" CACHE BOOL "Set to ON to skip cURL checks and force building ZM without cURL. default: OFF") set(ZM_NO_X10 "OFF" CACHE BOOL @@ -321,7 +325,7 @@ if(NOT ZM_NO_CURL) find_package(CURL) if(CURL_FOUND) set(HAVE_LIBCURL 1) - list(APPEND ZM_BIN_LIBS ${CURL_LIBRARIES}) + #list(APPEND ZM_BIN_LIBS ${CURL_LIBRARIES}) include_directories(${CURL_INCLUDE_DIRS}) set(CMAKE_REQUIRED_INCLUDES ${CURL_INCLUDE_DIRS}) check_include_file("curl/curl.h" HAVE_CURL_CURL_H) @@ -349,19 +353,50 @@ else(JPEG_FOUND) "ZoneMinder requires jpeg but it was not found on your system") endif(JPEG_FOUND) +# LIBJWT +find_package(LibJWT) +if(LIBJWT_FOUND) + set(HAVE_LIBJWT 1) + set(optlibsfound "${optlibsfound} LIBJWT") + list(APPEND ZM_BIN_LIBS "${LIBJWT_LIBRARY}") +else(LIBJWT_FOUND) + set(optlibsnotfound "${optlibsnotfound} LIBJWT") +endif(LIBJWT_FOUND) + +# gnutls (using find_library and find_path) +if(HAVE_LIBJWT) + find_library(GNUTLS_LIBRARIES gnutls) + if(GNUTLS_LIBRARIES) + set(HAVE_LIBGNUTLS 1) + list(APPEND ZM_BIN_LIBS "${GNUTLS_LIBRARIES}") + find_path(GNUTLS_INCLUDE_DIR gnutls/gnutls.h) + if(GNUTLS_INCLUDE_DIR) + include_directories("${GNUTLS_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}") + endif(GNUTLS_INCLUDE_DIR) + mark_as_advanced(FORCE GNUTLS_LIBRARIES GNUTLS_INCLUDE_DIR) + check_include_file("gnutls/gnutls.h" HAVE_GNUTLS_GNUTLS_H) + set(optlibsfound "${optlibsfound} GnuTLS") + else(GNUTLS_LIBRARIES) + set(optlibsnotfound "${optlibsnotfound} GnuTLS") + endif(GNUTLS_LIBRARIES) +endif(HAVE_LIBJWT) + # OpenSSL -find_package(OpenSSL) -if(OPENSSL_FOUND) - set(HAVE_LIBOPENSSL 1) - set(HAVE_LIBCRYPTO 1) - list(APPEND ZM_BIN_LIBS "${OPENSSL_LIBRARIES}") - include_directories("${OPENSSL_INCLUDE_DIR}") - set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") - check_include_file("openssl/md5.h" HAVE_OPENSSL_MD5_H) - set(optlibsfound "${optlibsfound} OpenSSL") -else(OPENSSL_FOUND) - set(optlibsnotfound "${optlibsnotfound} OpenSSL") -endif(OPENSSL_FOUND) +if(NOT HAVE_LIBGNUTLS OR NOT HAVE_LIBJWT) + find_package(OpenSSL) + if(OPENSSL_FOUND) + set(HAVE_LIBOPENSSL 1) + set(HAVE_LIBCRYPTO 1) + list(APPEND ZM_BIN_LIBS "${OPENSSL_LIBRARIES}") + include_directories("${OPENSSL_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") + check_include_file("openssl/md5.h" HAVE_OPENSSL_MD5_H) + set(optlibsfound "${optlibsfound} OpenSSL") + else(OPENSSL_FOUND) + set(optlibsnotfound "${optlibsnotfound} OpenSSL") + endif(OPENSSL_FOUND) +endif(NOT HAVE_LIBGNUTLS OR NOT HAVE_LIBJWT) # pthread (using find_library and find_path) find_library(PTHREAD_LIBRARIES pthread) @@ -418,28 +453,6 @@ else(GCRYPT_LIBRARIES) set(optlibsnotfound "${optlibsnotfound} GCrypt") endif(GCRYPT_LIBRARIES) -# gnutls (using find_library and find_path) -find_library(GNUTLS_LIBRARIES gnutls-openssl) -if(NOT GNUTLS_LIBRARIES) - find_library(GNUTLS_LIBRARIES gnutls) -endif(NOT GNUTLS_LIBRARIES) - -if(GNUTLS_LIBRARIES) - set(HAVE_LIBGNUTLS 1) - list(APPEND ZM_BIN_LIBS "${GNUTLS_LIBRARIES}") - find_path(GNUTLS_INCLUDE_DIR gnutls/gnutls.h) - if(GNUTLS_INCLUDE_DIR) - include_directories("${GNUTLS_INCLUDE_DIR}") - set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}") - endif(GNUTLS_INCLUDE_DIR) - mark_as_advanced(FORCE GNUTLS_LIBRARIES GNUTLS_INCLUDE_DIR) - check_include_file("gnutls/openssl.h" HAVE_GNUTLS_OPENSSL_H) - check_include_file("gnutls/gnutls.h" HAVE_GNUTLS_GNUTLS_H) - set(optlibsfound "${optlibsfound} GnuTLS") -else(GNUTLS_LIBRARIES) - set(optlibsnotfound "${optlibsnotfound} GnuTLS") -endif(GNUTLS_LIBRARIES) - # mysqlclient (using find_library and find_path) find_library(MYSQLCLIENT_LIBRARIES mysqlclient PATH_SUFFIXES mysql) if(MYSQLCLIENT_LIBRARIES) @@ -547,6 +560,7 @@ if(AVCODEC_LIBRARIES) check_include_file("libavcodec/avcodec.h" HAVE_LIBAVCODEC_AVCODEC_H) set(optlibsfound "${optlibsfound} AVCodec") else(AVCODEC_LIBRARIES) + message(WARNING "\nWhile it should be possible to build ZM without AVCODEC the result will pretty useless.") set(optlibsnotfound "${optlibsnotfound} AVCodec") endif(AVCODEC_LIBRARIES) @@ -654,7 +668,7 @@ if(NOT ZM_NO_LIBVLC) find_library(LIBVLC_LIBRARIES vlc) if(LIBVLC_LIBRARIES) set(HAVE_LIBVLC 1) - list(APPEND ZM_BIN_LIBS "${LIBVLC_LIBRARIES}") + #list(APPEND ZM_BIN_LIBS "${LIBVLC_LIBRARIES}") find_path(LIBVLC_INCLUDE_DIR "vlc/vlc.h") if(LIBVLC_INCLUDE_DIR) include_directories("${LIBVLC_INCLUDE_DIR}") @@ -668,6 +682,25 @@ if(NOT ZM_NO_LIBVLC) endif(LIBVLC_LIBRARIES) endif(NOT ZM_NO_LIBVLC) +if(NOT ZM_NO_LIBVNC) + # libvncclient (using find_library and find_path) + find_library(LIBVNC_LIBRARIES vncclient) + if(LIBVNC_LIBRARIES) + set(HAVE_LIBVNC 1) + #list(APPEND ZM_BIN_LIBS "${LIBVNC_LIBRARIES}") + find_path(LIBVNC_INCLUDE_DIR "rfb/rfb.h") + if(LIBVNC_INCLUDE_DIR) + include_directories("${LIBVNC_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${LIBVNC_INCLUDE_DIR}") + endif(LIBVNC_INCLUDE_DIR) + mark_as_advanced(FORCE LIBVNC_LIBRARIES LIBVNC_INCLUDE_DIR) + check_include_file("rfb/rfb.h" HAVE_RFB_RFB_H) + set(optlibsfound "${optlibsfound} libVNC") + else(LIBVNC_LIBRARIES) + set(optlibsnotfound "${optlibsnotfound} libVNC") + endif(LIBVNC_LIBRARIES) +endif(NOT ZM_NO_LIBVNC) + #find_package(Boost 1.36.0) #if(Boost_FOUND) #include_directories(${Boost_INCLUDE_DIRS}) @@ -733,14 +766,7 @@ if(HAVE_OPENSSL_MD5_H) "unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md)" "NULL" "openssl/md5.h" HAVE_MD5_OPENSSL) endif(HAVE_OPENSSL_MD5_H) -if(HAVE_GNUTLS_OPENSSL_H) - set(CMAKE_REQUIRED_LIBRARIES "${GNUTLS_LIBRARIES}") - set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}") - check_prototype_definition( - MD5 - "unsigned char *MD5 (const unsigned char *buf, unsigned long len, unsigned char *md)" "NULL" "gnutls/openssl.h" - HAVE_MD5_GNUTLS) -endif(HAVE_GNUTLS_OPENSSL_H) + if(HAVE_GNUTLS_GNUTLS_H) set(CMAKE_REQUIRED_LIBRARIES "${GNUTLS_LIBRARIES}") set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}") @@ -749,13 +775,17 @@ if(HAVE_GNUTLS_GNUTLS_H) "int gnutls_fingerprint (gnutls_digest_algorithm_t algo, const gnutls_datum_t * data, void *result, size_t * result_size)" "0" "stdlib.h;gnutls/gnutls.h" HAVE_DECL_GNUTLS_FINGERPRINT) endif(HAVE_GNUTLS_GNUTLS_H) -if(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS) + +if(NOT HAVE_DECL_GNUTLS_FINGERPRINT AND HAVE_MD5_OPENSSL) set(HAVE_DECL_MD5 1) -else(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS) +endif(NOT HAVE_DECL_GNUTLS_FINGERPRINT AND HAVE_MD5_OPENSSL) + +if((NOT HAVE_MD5_OPENSSL) AND (NOT HAVE_DECL_GNUTLS_FINGERPRINT)) message(AUTHOR_WARNING - "ZoneMinder requires a working MD5 function for hashed authenication but - none were found - hashed authenication will not be available") -endif(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS) + "ZoneMinder requires a working MD5 function for hashed authentication but + none were found - hashed authentication will not be available") +endif((NOT HAVE_MD5_OPENSSL) AND (NOT HAVE_DECL_GNUTLS_FINGERPRINT)) + # Dirty fix for zm_user only using openssl's md5 if gnutls and gcrypt are not available. # This needs to be fixed in zm_user.[h,cpp] but such fix will also require changes to configure.ac if(HAVE_LIBCRYPTO AND HAVE_OPENSSL_MD5_H AND HAVE_MD5_OPENSSL) @@ -871,7 +901,6 @@ configure_file(zmlinkcontent.sh.in "${CMAKE_CURRENT_BINARY_DIR}/zmlinkcontent.sh # Create a target for man pages include(Pod2Man) -ADD_MANPAGE_TARGET() # Process subdirectories @@ -886,11 +915,7 @@ add_subdirectory(scripts) add_subdirectory(db) add_subdirectory(web) add_subdirectory(misc) - -# Enable ONVIF support -if(ZM_ONVIF) - add_subdirectory(onvif) -endif(ZM_ONVIF) +add_subdirectory(onvif) # Process distro subdirectories if((ZM_TARGET_DISTRO MATCHES "^el") OR (ZM_TARGET_DISTRO MATCHES "^fc")) @@ -905,7 +930,7 @@ message(STATUS "Optional libraries not found:${optlibsnotfound}") # Run ZM configuration generator message(STATUS "Running ZoneMinder configuration generator") -execute_process(COMMAND perl ./zmconfgen.pl RESULT_VARIABLE zmconfgen_result) +execute_process(COMMAND perl ${CMAKE_CURRENT_BINARY_DIR}/zmconfgen.pl RESULT_VARIABLE zmconfgen_result) if(zmconfgen_result EQUAL 0) message(STATUS "ZoneMinder configuration generator completed successfully") diff --git a/README.md b/README.md index b0f4be37b..c7bd801b7 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ ZoneMinder ========== -[![Build Status](https://travis-ci.org/ZoneMinder/zoneminder.png)](https://travis-ci.org/ZoneMinder/zoneminder) [![Bountysource](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received) - -[![Join Slack](https://github.com/ozonesecurity/ozonebase/blob/master/img/slacksm.png?raw=true)](https://join.slack.com/t/zoneminder-chat/shared_invite/enQtNTU0NDkxMDM5NDQwLTlhZDU2MGU4MmZmN2MxOTg1MmNmNmZjZGRmY2EzMThhNGQ0MWNmZTg1ZmYzNDQ4YjliMzVmYTQ3MDc5MTkzODE) +[![Build Status](https://travis-ci.org/ZoneMinder/zoneminder.png)](https://travis-ci.org/ZoneMinder/zoneminder) +[![Bounty Source](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received) +[![Join Slack](https://github.com/ozonesecurity/ozonebase/blob/master/img/slacksm.png?raw=true)](https://join.slack.com/t/zoneminder-chat/shared_invite/enQtNTU0NDkxMDM5NDQwLTdhZmQ5Y2M2NWQyN2JkYTBiN2ZkMzIzZGQ0MDliMTRmM2FjZWRlYzUwYTQ2MjMwMTVjMzQ1NjYxOTdmMjE2MTE) +[![IRC Network](https://img.shields.io/badge/irc-%23zoneminder-blue.svg "IRC Freenode")](https://webchat.freenode.net/?channels=zoneminder) All documentation for ZoneMinder is now online at https://zoneminder.readthedocs.org @@ -21,17 +22,11 @@ https://github.com/ZoneMinder/zmdockerfiles ## Installation Methods -### Building from Source is Discouraged - -Historically, installing ZoneMinder onto your system required building from source code by issuing the traditional configure, make, make install commands. To get ZoneMinder to build, all of its dependencies had to be determined and installed beforehand. Init and logrotate scripts had to be manually copied into place following the build. Optional packages such as jscalendar and Cambozola had to be manually installed. Uninstalls could leave stale files around, which could cause problems during an upgrade. Speaking of upgrades, when it comes time to upgrade all these manual steps must be repeated again. - -Better methods exist today that do much of this for you. The current development team, along with other volunteers, have taken great strides in providing the resources necessary to avoid building from source. - ### Install from a Package Repository This is the recommended method to install ZoneMinder onto your system. ZoneMinder packages are maintained for the following distros: -- Ubuntu via [Iconnor's PPA](https://launchpad.net/~iconnor/+archive/ubuntu/zoneminder) +- Ubuntu via [Iconnor's PPA](https://launchpad.net/~iconnor) - Debian from their [default repository](https://packages.debian.org/search?searchon=names&keywords=zoneminder) - RHEL/CentOS and clones via [RPM Fusion](http://rpmfusion.org) - Fedora via [RPM Fusion](http://rpmfusion.org) @@ -42,6 +37,13 @@ This is the recommended method to install ZoneMinder onto your system. ZoneMinde If a repository that hosts ZoneMinder packages is not available for your distro, then you are encouraged to build your own package, rather than build from source. While each distro is different in ways that set it apart from all the others, they are often similar enough to allow you to adapt another distro's package building instructions to your own. +### Building from Source is Discouraged + +Historically, installing ZoneMinder onto your system required building from source code by issuing the traditional configure, make, make install commands. To get ZoneMinder to build, all of its dependencies had to be determined and installed beforehand. Init and logrotate scripts had to be manually copied into place following the build. Optional packages such as jscalendar and Cambozola had to be manually installed. Uninstalls could leave stale files around, which could cause problems during an upgrade. Speaking of upgrades, when it comes time to upgrade all these manual steps must be repeated again. + +Better methods exist today that do much of this for you. The current development team, along with other volunteers, have taken great strides in providing the resources necessary to avoid building from source. + + ### Building a ZoneMinder Package ### Building ZoneMinder into a package is not any harder than building from source. As a matter of fact, if you have successfully built ZoneMinder from source in the past, then you may find these steps to be easier. @@ -68,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. +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/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..c3b4c7211 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,18 @@ +# Security Policy + +## Supported Versions + +Time and computers move on. We do not have the resources to support every ancient version of everything +(unless you'd like to pay us to do so.) We ONLY support the latest stable release and development releases. + +| Version | Supported | +| ------- | ------------------ | +| 1.34.x | :white_check_mark: | +| 1.35.x | :white_check_mark: | +| < 1.34.x | :x: | + +## Reporting a Vulnerability + +Since sometimes security vulnerabilities can be sensitive, you can just email me at isaac@zoneminder.com. +If it's not such a big deal, by all means, create an issue here on github + diff --git a/cmake/Modules/FindLibJWT.cmake b/cmake/Modules/FindLibJWT.cmake new file mode 100644 index 000000000..e0c834609 --- /dev/null +++ b/cmake/Modules/FindLibJWT.cmake @@ -0,0 +1,28 @@ +include(FindPackageHandleStandardArgs) + +find_package(PkgConfig QUIET) +pkg_check_modules(PC_LIBJWT QUIET libjwt) + +find_path(LIBJWT_INCLUDE_DIR + NAMES jwt.h + HINTS ${PC_LIBJWT_INCLUDEDIR} ${PC_LIBJWT_INCLUDE_DIRS} + ) + +find_library(LIBJWT_LIBRARY + NAMES jwt-gnutls libjwt-gnutls liblibjwt-gnutls + HINTS ${PC_LIBJWT_LIBDIR} ${PC_LIBJWT_LIBRARY_DIR} + ) + +find_package_handle_standard_args(LibJWT + REQUIRED_VARS LIBJWT_INCLUDE_DIR LIBJWT_LIBRARY + ) + +if(LIBJWT_FOUND) + add_library(libjwt STATIC IMPORTED GLOBAL) + set_target_properties(libjwt PROPERTIES + IMPORTED_LOCATION "${LIBJWT_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${LIBJWT_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced(LIBJWT_INCLUDE_DIR LIBJWT_LIBRARY) \ No newline at end of file diff --git a/cmake/Modules/Pod2Man.cmake b/cmake/Modules/Pod2Man.cmake index 734be239b..f1c0e400a 100644 --- a/cmake/Modules/Pod2Man.cmake +++ b/cmake/Modules/Pod2Man.cmake @@ -53,8 +53,7 @@ MACRO(POD2MAN PODFILE MANFILE SECTION MANPAGE_DEST_PREFIX) SET(MANPAGE_TARGET "man-${MANFILE}") - ADD_CUSTOM_TARGET(${MANPAGE_TARGET} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}.gz) - ADD_DEPENDENCIES(man ${MANPAGE_TARGET}) + ADD_CUSTOM_TARGET(${MANPAGE_TARGET} ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}.gz) INSTALL( FILES ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}.gz diff --git a/conf.d/01-system-paths.conf.in b/conf.d/01-system-paths.conf.in index e1aaf0bef..277f63b70 100644 --- a/conf.d/01-system-paths.conf.in +++ b/conf.d/01-system-paths.conf.in @@ -46,3 +46,6 @@ ZM_PATH_SWAP=@ZM_TMPDIR@ # Full path to optional arp binary # ZoneMinder will find the arp binary automatically on most systems ZM_PATH_ARP="@ZM_PATH_ARP@" + +#Full path to shutdown binary +ZM_PATH_SHUTDOWN="@ZM_PATH_SHUTDOWN@" diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index e5e4d8c3d..092fce14f 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -63,7 +63,7 @@ DROP TABLE IF EXISTS `Controls`; CREATE TABLE `Controls` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', - `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL default 'Local', + `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local', `Protocol` varchar(64) default NULL, `CanWake` tinyint(3) unsigned NOT NULL default '0', `CanSleep` tinyint(3) unsigned NOT NULL default '0', @@ -209,7 +209,7 @@ CREATE TABLE `Events` ( `Executed` tinyint(3) unsigned NOT NULL default '0', `Notes` text, `StateId` int(10) unsigned NOT NULL, - `Orientation` enum('0','90','180','270','hori','vert') NOT NULL default '0', + `Orientation` enum('ROTATE_0','ROTATE_90','ROTATE_180','ROTATE_270','FLIP_HORI','FLIP_VERT') NOT NULL default 'ROTATE_0', `DiskSpace` bigint unsigned default NULL, `Scheme` enum('Deep','Medium','Shallow') NOT NULL default 'Medium', `Locked` BOOLEAN NOT NULL DEFAULT False, @@ -282,17 +282,23 @@ DROP TABLE IF EXISTS `Filters`; CREATE TABLE `Filters` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', - `Query` text NOT NULL, + `UserId` int(10) unsigned, + `Query_json` text NOT NULL, `AutoArchive` tinyint(3) unsigned NOT NULL default '0', `AutoVideo` tinyint(3) unsigned NOT NULL default '0', `AutoUpload` tinyint(3) unsigned NOT NULL default '0', `AutoEmail` tinyint(3) unsigned NOT NULL default '0', + `EmailTo` TEXT, + `EmailSubject` TEXT, + `EmailBody` TEXT, `AutoMessage` tinyint(3) unsigned NOT NULL default '0', `AutoExecute` tinyint(3) unsigned NOT NULL default '0', `AutoExecuteCmd` tinytext, `AutoDelete` tinyint(3) unsigned NOT NULL default '0', `AutoMove` tinyint(3) unsigned NOT NULL default '0', `AutoMoveTo` smallint(5) unsigned NOT NULL default 0, + `AutoCopy` tinyint(3) unsigned NOT NULL default '0', + `AutoCopyTo` smallint(5) unsigned NOT NULL default 0, `UpdateDiskSpace` tinyint(3) unsigned NOT NULL default '0', `Background` tinyint(1) unsigned NOT NULL default '0', `Concurrent` tinyint(1) unsigned NOT NULL default '0', @@ -401,7 +407,7 @@ DROP TABLE IF EXISTS `MonitorPresets`; CREATE TABLE `MonitorPresets` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', - `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL default 'Local', + `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local', `Device` tinytext, `Channel` tinyint(3) unsigned default NULL, `Format` int(10) unsigned default NULL, @@ -432,13 +438,18 @@ 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', + `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local', `Function` enum('None','Monitor','Modect','Record','Mocord','Nodect') NOT NULL default 'Monitor', `Enabled` tinyint(3) unsigned NOT NULL default '1', `LinkedMonitors` varchar(255), `Triggers` set('X10') NOT NULL default '', + `ONVIF_URL` VARCHAR(255) NOT NULL DEFAULT '', + `ONVIF_Username` VARCHAR(64) NOT NULL DEFAULT '', + `ONVIF_Password` VARCHAR(64) NOT NULL DEFAULT '', + `ONVIF_Options` VARCHAR(64) NOT NULL DEFAULT '', `Device` tinytext NOT NULL default '', `Channel` tinyint(3) unsigned NOT NULL default '0', `Format` int(10) unsigned NOT NULL default '0', @@ -457,7 +468,7 @@ CREATE TABLE `Monitors` ( `Height` smallint(5) unsigned NOT NULL default '0', `Colours` tinyint(3) unsigned NOT NULL default '1', `Palette` int(10) unsigned NOT NULL default '0', - `Orientation` enum('0','90','180','270','hori','vert') NOT NULL default '0', + `Orientation` enum('ROTATE_0','ROTATE_90','ROTATE_180','ROTATE_270','FLIP_HORI','FLIP_VERT') NOT NULL default 'ROTATE_0', `Deinterlacing` int(10) unsigned NOT NULL default '0', `DecoderHWAccelName` varchar(64), `DecoderHWAccelDevice` varchar(255), @@ -474,7 +485,7 @@ CREATE TABLE `Monitors` ( `Hue` mediumint(7) NOT NULL default '-1', `Colour` mediumint(7) NOT NULL default '-1', `EventPrefix` varchar(32) NOT NULL default 'Event-', - `LabelFormat` varchar(64) default '%N - %y/%m/%d %H:%M:%S', + `LabelFormat` varchar(64), `LabelX` smallint(5) unsigned NOT NULL default '0', `LabelY` smallint(5) unsigned NOT NULL default '0', `LabelSize` smallint(5) unsigned NOT NULL DEFAULT '1', @@ -728,13 +739,14 @@ CREATE TABLE `Storage` ( `Scheme` enum('Deep','Medium','Shallow') NOT NULL default 'Medium', `ServerId` int(10) unsigned, `DoDelete` BOOLEAN NOT NULL DEFAULT true, + `Enabled` BOOLEAN NOT NULL DEFAULT true, PRIMARY KEY (`Id`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- -- Create a default storage location -- -insert into Storage VALUES (NULL, '@ZM_DIR_EVENTS@', 'Default', 'local', NULL, NULL, 'Medium', 0, true ); +insert into Storage VALUES (NULL, '@ZM_DIR_EVENTS@', 'Default', 'local', NULL, NULL, 'Medium', 0, true, true ); /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; @@ -757,8 +769,95 @@ insert into Users VALUES (NULL,'admin','$2b$12$NHZsm6AM2f2LQVROriz79ul3D6DnmFiZC -- Add a sample filter to purge the oldest 100 events when the disk is 95% full -- -insert into Filters values (NULL,'PurgeWhenFull','{"sort_field":"Id","terms":[{"val":0,"attr":"Archived","op":"="},{"cnj":"and","val":95,"attr":"DiskPercent","op":">="}],"limit":100,"sort_asc":1}',0/*AutoArchive*/,0/*AutoVideo*/,0/*AutoUpload*/,0/*AutoEmail*/,0/*AutoMessage*/,0/*AutoExecute*/,'',1/*AutoDelete*/,0/*AutoMove*/,0/*MoveTo*/,0/*UpdateDiskSpace*/,1/*Background*/,0/*Concurrent*/); -insert into Filters values (NULL,'Update DiskSpace','{"terms":[{"attr":"DiskSpace","op":"IS","val":"NULL"}]}',0,0,0,0,0,0,'',0,0,0,1,1,0); +INSERT INTO `Filters` + ( + `Name`, + `Query_json`, + `AutoArchive`, + `AutoVideo`, + `AutoUpload`, + `AutoEmail`, + `EmailTo`, + `EmailSubject`, + `EmailBody`, + `AutoMessage`, + `AutoExecute`, + `AutoExecuteCmd`, + `AutoDelete`, + `AutoMove`, + `AutoMoveTo`, + `AutoCopy`, + `AutoCopyTo`, + `UpdateDiskSpace`, + `UserId`, + `Background`, + `Concurrent` + ) + VALUES + ( + 'PurgeWhenFull', + '{"sort_field":"Id","terms":[{"val":0,"attr":"Archived","op":"="},{"cnj":"and","val":95,"attr":"DiskPercent","op":">="}],"limit":100,"sort_asc":1}', + 0/*AutoArchive*/, + 0/*AutoVideo*/, + 0/*AutoUpload*/, + 0/*AutoEmail*/, + ''/*EmailTo*/, + ''/*EmailSubject*/, + ''/*EmailBody*/, + 0/*AutoMessage*/, + 0/*AutoExecute*/,'', + 1/*AutoDelete*/, + 0/*AutoMove*/,0/*MoveTo*/, + 0/*AutoCopy*/,0/*CopyTo*/, + 0/*UpdateDiskSpace*/, + 1/*UserId = admin*/, + 1/*Background*/, + 0/*Concurrent*/ + ); +INSERT INTO `Filters` + ( + `Name`, + `Query_json`, + `AutoArchive`, + `AutoVideo`, + `AutoUpload`, + `AutoEmail`, + `EmailTo`, + `EmailSubject`, + `EmailBody`, + `AutoMessage`, + `AutoExecute`, + `AutoExecuteCmd`, + `AutoDelete`, + `AutoMove`, + `AutoMoveTo`, + `AutoCopy`, + `AutoCopyTo`, + `UpdateDiskSpace`, + `UserId`, + `Background`, + `Concurrent` + ) +VALUES ( + 'Update DiskSpace', + '{"terms":[{"attr":"DiskSpace","op":"IS","val":"NULL"}]}', + 0/*AutoArchive*/, + 0/*AutoVideo*/, + 0/*AutoUpload*/, + 0/*AutoEmail*/, + ''/*EmailTo*/, + ''/*EmailSubject*/, + ''/*EmailBody*/, + 0/*AutoMessage*/, + 0/*AutoExecute*/,'', + 0/*AutoDelete*/, + 0/*AutoMove*/,0/*MoveTo*/, + 0/*AutoCopy*/,0/*CopyTo*/, + 1/*UpdateDiskSpace*/, + 1/*UserId=admin*/, + 1/*Background*/, + 0/*Concurrent*/ +); -- -- Add in some sample control protocol definitions @@ -796,6 +895,7 @@ INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-423','Ffmpeg','Reolink',0,0,1,0 INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-411','Ffmpeg','Reolink',0,0,1,0,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-420','Ffmpeg','Reolink',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'D-LINK DCS-3415','Remote','DCS3415',0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'D-Link DCS-5020L','Remote','DCS5020L',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,24,1,0,1,1,1,0,1,0,1,0,0,1,30,0,0,0,0,0,1,0,0,1,30,0,0,0,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'Dericam P2','Ffmpeg','DericamP2',0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,45,0,0,1,0,0,0,0,1,1,45,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'Trendnet','Remote','Trendnet',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); @@ -807,6 +907,9 @@ INSERT INTO `Controls` VALUES (NULL,'Amcrest HTTP API','Ffmpeg','Amcrest_HTTP',0 -- -- Add some monitor preset values -- + +INSERT into MonitorPresets VALUES (NULL,'Amcrest, IP8M-T2499EW 640x480, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp','NULL',554,'rtsp://:@/cam/realmonitor?channel=1&subtype=1',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100); +INSERT into MonitorPresets VALUES (NULL,'Amcrest, IP8M-T2499EW 3840x2160, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp','NULL',554,'rtsp://:@/cam/realmonitor?channel=1&subtype=0',NULL,3840,2160,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, mpjpeg','Remote','http',0,0,'http','simple','',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&req_fps=5',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, jpeg','Remote','http',0,0,'http','simple','',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); @@ -830,6 +933,7 @@ INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, multicast','Remote','rt INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, RTP/RTSP','Remote','rtsp',0,255,'rtsp','rtpRtsp','',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, RTP/RTSP/HTTP','Remote',NULL,NULL,NULL,'rtsp','rtpRtspHttp','',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'D-link DCS-930L, 640x480, mjpeg','Remote','http',0,0,'http','simple','',80,'/mjpeg.cgi',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100); +INSERT INTO MonitorPresets VALUES (NULL,'D-Link DCS-5020L, 640x480, mjpeg','Remote','http',0,0,'http','simple',':@','80','/video.cgi',NULL,640,480,0,NULL,1,'34',NULL,':@',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, mpjpeg','Remote','http',0,0,'http','simple','',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg','Remote','http',0,0,'http','simple','',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,0,NULL,NULL,NULL,100,100); diff --git a/db/zm_update-1.27.99.0.sql b/db/zm_update-1.27.99.0.sql deleted file mode 100644 index 5b5e495b6..000000000 --- a/db/zm_update-1.27.99.0.sql +++ /dev/null @@ -1,206 +0,0 @@ --- --- This updates a 1.27.0 database to 1.27.1 --- - --- --- Add Controls definition for Wanscam --- -INSERT INTO Controls -SELECT * FROM (SELECT NULL as Id, - 'WanscamPT' as Name, - 'Remote' as Type, - 'Wanscam' as Protocol, - 1 as CanWake, - 1 as CanSleep, - 1 as CanReset, - 0 as CanZoom, - 0 as CanAutoZoom, - 0 as CanZoomAbs, - 0 as CanZoomRel, - 0 as CanZoomCon, - 0 as MinZoomRange, - 0 as MaxZoomRange, - 0 as MinZoomStep, - 0 as MaxZoomStep, - 0 as HasZoomSpeed, - 0 as MinZoomSpeed, - 0 as MaxZoomSpeed, - 0 as CanFocus, - 0 as CanAutoFocus, - 0 as CanFocusAbs, - 0 as CanFocusRel, - 0 as CanFocusCon, - 0 as MinFocusRange, - 0 as MaxFocusRange, - 0 as MinFocusStep, - 0 as MaxFocusStep, - 0 as HasFocusSpeed, - 0 as MinFocusSpeed, - 0 as MaxFocusSpeed, - 1 as CanIris, - 0 as CanAutoIris, - 1 as CanIrisAbs, - 0 as CanIrisRel, - 0 as CanIrisCon, - 0 as MinIrisRange, - 16 as MaxIrisRange, - 0 as MinIrisStep, - 0 as MaxIrisStep, - 0 as HasIrisSpeed, - 0 as MinIrisSpeed, - 0 as MaxIrisSpeed, - 0 as CanGain, - 0 as CanAutoGain, - 0 as CanGainAbs, - 0 as CanGainRel, - 0 as CanGainCon, - 0 as MinGainRange, - 0 as MaxGainRange, - 0 as MinGainStep, - 0 as MaxGainStep, - 0 as HasGainSpeed, - 0 as MinGainSpeed, - 0 as MaxGainSpeed, - 1 as CanWhite, - 0 as CanAutoWhite, - 1 as CanWhiteAbs, - 0 as CanWhiteRel, - 0 as CanWhiteCon, - 0 as MinWhiteRange, - 16 as MaxWhiteRange, - 0 as MinWhiteStep, - 0 as MaxWhiteStep, - 0 as HasWhiteSpeed, - 0 as MinWhiteSpeed, - 0 as MaxWhiteSpeed, - 1 as HasPresets, - 16 as NumPresets, - 1 as HasHomePreset, - 1 as CanSetPresets, - 1 as CanMove, - 1 as CanMoveDiag, - 0 as CanMoveMap, - 0 as CanMoveAbs, - 0 as CanMoveRel, - 1 as CanMoveCon, - 1 as CanPan, - 0 as MinPanRange, - 0 as MaxPanRange, - 0 as MinPanStep, - 0 as MaxPanStep, - 0 as HasPanSpeed, - 0 as MinPanSpeed, - 0 as MaxPanSpeed, - 0 as HasTurboPan, - 0 as TurboPanSpeed, - 1 as CanTilt, - 0 as MinTiltRange, - 0 as MaxTiltRange, - 0 as MinTiltStep, - 0 as MaxTiltStep, - 0 as HasTiltSpeed, - 0 as MinTiltSpeed, - 0 as MaxTiltSpeed, - 0 as HasTurboTilt, - 0 as TurboTiltSpeed, - 0 as CanAutoScan, - 0 as NumScanPaths) AS tmp -WHERE NOT EXISTS ( - SELECT Name FROM Controls WHERE name = 'WanscamPT' -) LIMIT 1; - --- Add extend alarm frame count to zone definition and Presets -SET @s = (SELECT IF( - (SELECT COUNT(*) - FROM INFORMATION_SCHEMA.COLUMNS - WHERE table_name = 'Zones' - AND table_schema = DATABASE() - AND column_name = 'ExtendAlarmFrames' - ) > 0, -"SELECT 'Column ExtendAlarmFrames exists in Zones'", -"ALTER TABLE `Zones` ADD `ExtendAlarmFrames` smallint(5) unsigned not null default 0 AFTER `OverloadFrames`" -)); - -PREPARE stmt FROM @s; -EXECUTE stmt; - -SET @s = (SELECT IF( - (SELECT COUNT(*) - FROM INFORMATION_SCHEMA.COLUMNS - WHERE table_name = 'ZonePresets' - AND table_schema = DATABASE() - AND column_name = 'ExtendAlarmFrames' - ) > 0, -"SELECT 'Column ExtendAlarmFrames exists in ZonePresets'", -"ALTER TABLE `ZonePresets` ADD `ExtendAlarmFrames` smallint(5) unsigned not null default 0 AFTER `OverloadFrames`" -)); - -PREPARE stmt FROM @s; -EXECUTE stmt; - --- --- Add MotionSkipFrame field for controlling how many frames motion detection should skip. --- - -SET @s = (SELECT IF( - (SELECT COUNT(*) - FROM INFORMATION_SCHEMA.COLUMNS - WHERE table_name = 'Monitors' - AND table_schema = DATABASE() - AND column_name = 'MotionFrameSkip' - ) > 0, -"SELECT 1", -"ALTER TABLE `Monitors` ADD `MotionFrameSkip` smallint(5) unsigned NOT NULL default '0' AFTER `FrameSkip`" -)); - -PREPARE stmt FROM @s; -EXECUTE stmt; - --- --- Add Monitor Options field; used for specifying Ffmpeg AVoptions like rtsp_transport http or libVLC options --- -SET @s = (SELECT IF( - (SELECT COUNT(*) - FROM INFORMATION_SCHEMA.COLUMNS - WHERE table_name = 'Monitors' - AND table_schema = DATABASE() - AND column_name = 'Options' - ) > 0, -"SELECT 'Column Options already exists in Monitors'", -"ALTER TABLE `Monitors` ADD `Options` varchar(255) not null default '' AFTER `Path`" -)); - -PREPARE stmt FROM @s; -EXECUTE stmt; - --- --- Add V4LMultiBuffer and V4LCapturesPerFrame to Monitor --- - -SET @s = (SELECT IF( - (SELECT COUNT(*) - FROM INFORMATION_SCHEMA.COLUMNS - WHERE table_name = 'Monitors' - AND table_schema = DATABASE() - AND column_name = 'V4LMultiBuffer' - ) > 0, -"SELECT 'Column V4LMultiBuffer exists in Monitors'", -"ALTER TABLE `Monitors` ADD `V4LMultiBuffer` tinyint(1) unsigned AFTER `Format`" -)); - -PREPARE stmt FROM @s; -EXECUTE stmt; - -SET @s = (SELECT IF( - (SELECT COUNT(*) - FROM INFORMATION_SCHEMA.COLUMNS - WHERE table_name = 'Monitors' - AND table_schema = DATABASE() - AND column_name = 'V4LCapturesPerFrame' - ) > 0, -"SELECT 'Column V4LCapturesPerFrame exists in Monitors'", -"ALTER TABLE `Monitors` ADD `V4LCapturesPerFrame` tinyint(3) unsigned AFTER `V4LMultiBuffer`" -)); - -PREPARE stmt FROM @s; -EXECUTE stmt; 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.33.15.sql b/db/zm_update-1.33.15.sql new file mode 100644 index 000000000..56b26fa2d --- /dev/null +++ b/db/zm_update-1.33.15.sql @@ -0,0 +1,19 @@ +ALTER TABLE Events MODIFY `Orientation` enum('0','90','180','270','hori','vert','ROTATE_0','ROTATE_90','ROTATE_180','ROTATE_270','FLIP_HORI','FLIP_VERT') NOT NULL default 'ROTATE_0'; +UPDATE Events SET Orientation='ROTATE_0' WHERE Orientation='0'; +UPDATE Events SET Orientation='ROTATE_90' WHERE Orientation='90'; +UPDATE Events SET Orientation='ROTATE_180' WHERE Orientation='180'; +UPDATE Events SET Orientation='ROTATE_270' WHERE Orientation='270'; +UPDATE Events SET Orientation='FLIP_HORI' WHERE Orientation='hori'; +UPDATE Events SET Orientation='FLIP_VERT' WHERE Orientation='vert'; + +ALTER TABLE Events MODIFY `Orientation` enum('ROTATE_0','ROTATE_90','ROTATE_180','ROTATE_270','FLIP_HORI','FLIP_VERT') NOT NULL default 'ROTATE_0'; + +ALTER TABLE Monitors MODIFY `Orientation` enum('0','90','180','270','hori','vert','ROTATE_0','ROTATE_90','ROTATE_180','ROTATE_270','FLIP_HORI','FLIP_VERT') NOT NULL default 'ROTATE_0'; +UPDATE Monitors SET Orientation='ROTATE_0' WHERE Orientation='0'; +UPDATE Monitors SET Orientation='ROTATE_90' WHERE Orientation='90'; +UPDATE Monitors SET Orientation='ROTATE_180' WHERE Orientation='180'; +UPDATE Monitors SET Orientation='ROTATE_270' WHERE Orientation='270'; +UPDATE Monitors SET Orientation='FLIP_HORI' WHERE Orientation='hori'; +UPDATE Monitors SET Orientation='FLIP_VERT' WHERE Orientation='vert'; + +ALTER TABLE Monitors MODIFY `Orientation` enum('ROTATE_0','ROTATE_90','ROTATE_180','ROTATE_270','FLIP_HORI','FLIP_VERT') NOT NULL default 'ROTATE_0'; 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/db/zm_update-1.34.0.sql b/db/zm_update-1.34.0.sql new file mode 100644 index 000000000..b8bd3e4ed --- /dev/null +++ b/db/zm_update-1.34.0.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.33.16 database to 1.34.0 +-- +-- No changes required +-- diff --git a/db/zm_update-1.34.1.sql b/db/zm_update-1.34.1.sql new file mode 100644 index 000000000..65ba2e5ff --- /dev/null +++ b/db/zm_update-1.34.1.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.34.0 database to 1.34.1 +-- +-- No changes required +-- diff --git a/db/zm_update-1.34.2.sql b/db/zm_update-1.34.2.sql new file mode 100644 index 000000000..1fcc882d2 --- /dev/null +++ b/db/zm_update-1.34.2.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.34.1 database to 1.34.2 +-- +-- No changes required +-- diff --git a/db/zm_update-1.34.20.sql b/db/zm_update-1.34.20.sql new file mode 100644 index 000000000..3512534be --- /dev/null +++ b/db/zm_update-1.34.20.sql @@ -0,0 +1,2 @@ +/* This was done in 1.31.0 but zm_create.sql.in wasn't updated to match. */ +ALTER TABLE Monitors MODIFY LinkedMonitors varchar(255); diff --git a/db/zm_update-1.34.3.sql b/db/zm_update-1.34.3.sql new file mode 100644 index 000000000..b84207047 --- /dev/null +++ b/db/zm_update-1.34.3.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.34.2 database to 1.34.3 +-- +-- No changes required +-- diff --git a/db/zm_update-1.34.4.sql b/db/zm_update-1.34.4.sql new file mode 100644 index 000000000..2910943a0 --- /dev/null +++ b/db/zm_update-1.34.4.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.34.3 database to 1.34.4 +-- +-- No changes required +-- diff --git a/db/zm_update-1.34.5.sql b/db/zm_update-1.34.5.sql new file mode 100644 index 000000000..7e12b977f --- /dev/null +++ b/db/zm_update-1.34.5.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.34.4 database to 1.34.5 +-- +-- No changes required +-- diff --git a/db/zm_update-1.34.6.sql b/db/zm_update-1.34.6.sql new file mode 100644 index 000000000..1a58bee1f --- /dev/null +++ b/db/zm_update-1.34.6.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.34.5 database to 1.34.6 +-- +-- No changes required +-- diff --git a/db/zm_update-1.34.7.sql b/db/zm_update-1.34.7.sql new file mode 100644 index 000000000..ba86b1202 --- /dev/null +++ b/db/zm_update-1.34.7.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.34.6 database to 1.34.7 +-- +-- No changes required +-- diff --git a/db/zm_update-1.35.0.sql b/db/zm_update-1.35.0.sql new file mode 100644 index 000000000..2e9132a63 --- /dev/null +++ b/db/zm_update-1.35.0.sql @@ -0,0 +1,41 @@ +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Filters' + AND column_name = 'EmailTo' + ) > 0, +"SELECT 'Column EmailTo already exists in Filters'", +"ALTER TABLE `Filters` ADD `EmailTo` TEXT AFTER `AutoEmail`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +UPDATE Filters SET EmailTo=(SELECT Value FROM Config WHERE Name='ZM_EMAIL_ADDRESS') WHERE AutoEmail=1; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Filters' + AND column_name = 'EmailSubject' + ) > 0, +"SELECT 'Column EmailSubject already exists in Filters'", +"ALTER TABLE `Filters` ADD `EmailSubject` TEXT AFTER `EmailTo`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +UPDATE Filters SET EmailSubject=(SELECT Value FROM Config WHERE Name='ZM_EMAIL_SUBJECT') WHERE AutoEmail=1; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Filters' + AND column_name = 'EmailBody' + ) > 0, +"SELECT 'Column EmailBody already exists in Filters'", +"ALTER TABLE `Filters` ADD `EmailBody` TEXT AFTER `EmailSubject`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +UPDATE Filters SET EmailBody=(SELECT Value FROM Config WHERE Name='ZM_EMAIL_BODY') WHERE AutoEmail=1; diff --git a/db/zm_update-1.35.1.sql b/db/zm_update-1.35.1.sql new file mode 100644 index 000000000..d250cf751 --- /dev/null +++ b/db/zm_update-1.35.1.sql @@ -0,0 +1,12 @@ + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Storage' + AND column_name = 'Enabled' + ) > 0, +"SELECT 'Column Enabled already exists in Storage'", +"ALTER TABLE `Storage` ADD `Enabled` BOOLEAN NOT NULL default true AFTER `DoDelete`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.35.2.sql b/db/zm_update-1.35.2.sql new file mode 100644 index 000000000..55eabd887 --- /dev/null +++ b/db/zm_update-1.35.2.sql @@ -0,0 +1 @@ +ALTER TABLE Monitors MODIFY `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local'; diff --git a/db/zm_update-1.35.3.sql b/db/zm_update-1.35.3.sql new file mode 100644 index 000000000..23f24dd3d --- /dev/null +++ b/db/zm_update-1.35.3.sql @@ -0,0 +1,2 @@ +SELECT 'ALTERING Frames.Id to a BIGINT. This could take a long time.'; +ALTER TABLE Frames MODIFY Id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT; diff --git a/db/zm_update-1.35.4.sql b/db/zm_update-1.35.4.sql new file mode 100644 index 000000000..44d195224 --- /dev/null +++ b/db/zm_update-1.35.4.sql @@ -0,0 +1,60 @@ +-- +-- This update adds ONVIF features +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'ONVIF_URL' + ) > 0, +"SELECT 'Column ONVIF_URL already exists in Monitors'", +"ALTER TABLE `Monitors` ADD COLUMN `ONVIF_URL` VARCHAR(255) NOT NULL DEFAULT '' AFTER `Triggers`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'ONVIF_Username' + ) > 0, +"SELECT 'Column ONVIF_Username already exists in Monitors'", +"ALTER TABLE `Monitors` ADD COLUMN `ONVIF_Username` VARCHAR(64) NOT NULL DEFAULT '' AFTER `ONVIF_URL`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'ONVIF_Password' + ) > 0, +"SELECT 'Column ONVIF_Password already exists in Monitors'", +"ALTER TABLE `Monitors` ADD COLUMN `ONVIF_Password` VARCHAR(64) NOT NULL DEFAULT '' AFTER `ONVIF_Username`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'ONVIF_Options' + ) > 0, +"SELECT 'Column ONVIF_Options already exists in Monitors'", +"ALTER TABLE `Monitors` ADD COLUMN `ONVIF_Options` VARCHAR(64) NOT NULL DEFAULT '' AFTER `ONVIF_Password`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + diff --git a/db/zm_update-1.35.5.sql b/db/zm_update-1.35.5.sql new file mode 100644 index 000000000..5507fcbfa --- /dev/null +++ b/db/zm_update-1.35.5.sql @@ -0,0 +1,11 @@ +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Filters' + AND column_name = 'UserId' + ) > 0, +"SELECT 'Column UserId already exists in Filters'", +"ALTER TABLE `Filters` ADD `UserId` int(10) unsigned AFTER `Name`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.35.6.sql b/db/zm_update-1.35.6.sql new file mode 100644 index 000000000..3512534be --- /dev/null +++ b/db/zm_update-1.35.6.sql @@ -0,0 +1,2 @@ +/* This was done in 1.31.0 but zm_create.sql.in wasn't updated to match. */ +ALTER TABLE Monitors MODIFY LinkedMonitors varchar(255); diff --git a/distros/beowulf/NEWS b/distros/beowulf/NEWS new file mode 100644 index 000000000..6200726cf --- /dev/null +++ b/distros/beowulf/NEWS @@ -0,0 +1,10 @@ +zoneminder (1.28.1-1) unstable; urgency=low + + This version is no longer automatically initialize or upgrade database. + See README.Debian for details. + + Changed installation paths (please correct your web server configuration): + /usr/share/zoneminder --> /usr/share/zoneminder/www + /usr/lib/cgi-bin --> /usr/lib/zoneminder/cgi-bin + + -- Dmitry Smirnov Tue, 31 Mar 2015 15:12:17 +1100 diff --git a/distros/ubuntu1204/README.Debian b/distros/beowulf/README.Debian similarity index 64% rename from distros/ubuntu1204/README.Debian rename to distros/beowulf/README.Debian index 2ba809fe4..4fe3464d2 100644 --- a/distros/ubuntu1204/README.Debian +++ b/distros/beowulf/README.Debian @@ -8,7 +8,7 @@ Initializing database OR cat /usr/share/zoneminder/db/zm_create.sql | sudo mysql --defaults-file=/etc/mysql/debian.cnf - echo 'grant lock tables,alter,create,select,insert,update,delete on zm.* to 'zmuser'@localhost identified by "zmpass";'\ + echo 'grant lock tables,alter,create,index,select,insert,update,delete on zm.* to 'zmuser'@localhost identified by "zmpass";'\ | sudo mysql --defaults-file=/etc/mysql/debian.cnf mysql Hint: generate secure password with `pwgen` and update "/etc/zm/zm.conf" @@ -23,8 +23,7 @@ configuration file: Upgrading database ------------------ -Prior to 1.28.1 database upgrade was performed automatically. -"zoneminder" service will refuse to start with outdated database. +The database is updated automatically on installation. You should not need to take this step. Assuming that database is on "localhost" then the following command can be used to upgrade "zm" database: @@ -45,17 +44,11 @@ The following command prints the current version of zoneminder database: Enabling service ---------------- -By default Zoneminder service is not starting automatically and need to be -manually activated once database is configured: - -On systemd: +By default Zoneminder service is not automatically started and needs to be +manually enabled once database is configured: sudo systemctl enable zoneminder.service -On SysV: - - sudo update-rc.d zoneminder enable - Web server set-up ----------------- @@ -82,10 +75,10 @@ Common configuration steps for Apache2: ## nginx / fcgiwrap -Nginx needs "php5-fpm" package to support PHP and "fcgiwrap" package +Nginx needs "php-fpm" package to support PHP and "fcgiwrap" package for binary "cgi-bin" applications: - sudo apt-get install php5-fpm fcgiwrap + sudo apt-get install php-fpm fcgiwrap To enable a URL alias that makes Zoneminder available from @@ -119,32 +112,9 @@ site configuration. Changing the location for images and events ------------------------------------------- -Zoneminder, in its upstream form, stores data in /usr/share/zoneminder/. This -package modifies that by changing /usr/share/zoneminder/images and -/usr/share/zoneminder/events to symlinks to directories under -/var/cache/zoneminder. - -There are numerous places these could be put and ways to do it. But, at the -moment, if you change this, an upgrade will fail with a warning about these -locations having changed (the reason for this was that previously, an upgrade -would silently revert the changes and cause event loss - refer -bug #608793). - -If you do want to change the location, here are a couple of suggestions. -(thanks to vagrant@freegeek.org): - -These lines in fstab could allow you to bind-mount an alternate location - - /dev/sdX1 /otherdrive ext3 defaults 0 2 - /otherdrive/zoneminder/images /var/cache/zoneminder/images bind defaults 0 2 - /otherdrive/zoneminder/events /var/cache/zoneminder/events bind defaults 0 2 - - or if you have a separate partition for each: - - /dev/sdX1 /var/cache/zoneminder/images ext3 defaults 0 2 - /dev/sdX2 /var/cache/zoneminder/events ext3 defaults 0 2 - - -- Peter Howard , Sun, 16 Jan 2010 01:35:51 +1100 +ZoneMinder is now able to be configured to use an alternative location for storing +events and images at compile time. This package makes use of that, so symlinks in +/usr/share/zoneminder/www are no longer necessary. Access to /dev/video* --------------------- diff --git a/distros/ubuntu1204/TODO.Debian b/distros/beowulf/TODO.Debian similarity index 100% rename from distros/ubuntu1204/TODO.Debian rename to distros/beowulf/TODO.Debian diff --git a/distros/beowulf/changelog b/distros/beowulf/changelog new file mode 100644 index 000000000..616f75178 --- /dev/null +++ b/distros/beowulf/changelog @@ -0,0 +1,3 @@ +zoneminder (1.31.39~20180223.27-stretch-1) unstable; urgency=low + * + -- Isaac Connor Fri, 23 Feb 2018 14:15:59 -0500 diff --git a/distros/ubuntu1204/clean b/distros/beowulf/clean similarity index 100% rename from distros/ubuntu1204/clean rename to distros/beowulf/clean diff --git a/distros/ubuntu1204/compat b/distros/beowulf/compat similarity index 100% rename from distros/ubuntu1204/compat rename to distros/beowulf/compat diff --git a/distros/ubuntu1204/conf/apache2/zoneminder.conf b/distros/beowulf/conf/apache2/zoneminder.conf similarity index 95% rename from distros/ubuntu1204/conf/apache2/zoneminder.conf rename to distros/beowulf/conf/apache2/zoneminder.conf index 8e2957cbf..e3164d36c 100644 --- a/distros/ubuntu1204/conf/apache2/zoneminder.conf +++ b/distros/beowulf/conf/apache2/zoneminder.conf @@ -6,7 +6,8 @@ ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin" Require all granted -# Order matters. This Alias must come first + +# Order matters. This alias must come first. Alias /zm/cache /var/cache/zoneminder/cache Options -Indexes +FollowSymLinks @@ -24,7 +25,6 @@ Alias /zm/cache /var/cache/zoneminder/cache Alias /zm /usr/share/zoneminder/www - php_flag register_globals off Options -Indexes +FollowSymLinks DirectoryIndex index.php diff --git a/distros/ubuntu1204/control b/distros/beowulf/control similarity index 79% rename from distros/ubuntu1204/control rename to distros/beowulf/control index cc6158334..dd410c0ba 100644 --- a/distros/ubuntu1204/control +++ b/distros/beowulf/control @@ -1,35 +1,41 @@ Source: zoneminder Section: net Priority: optional -Maintainer: Dmitry Smirnov -Uploaders: Vagrant Cascadian -Build-Depends: debhelper (>= 9), python-sphinx | python3-sphinx, apache2-dev, dh-linktree +Maintainer: Isaac Connor +Uploaders: Isaac Connor +Build-Depends: debhelper, sphinx-doc, dh-linktree, dh-apache2 ,cmake ,libx264-dev, libmp4v2-dev - ,libavcodec-dev, libavformat-dev (>= 3:0.svn20090204), libswscale-dev (>= 3:0.svn20090204), libavutil-dev, libavdevice-dev + ,libavdevice-dev + ,libavcodec-dev + ,libavformat-dev + ,libavutil-dev + ,libswresample-dev + ,libswscale-dev + ,ffmpeg + ,net-tools ,libbz2-dev - ,libgcrypt-dev + ,libgcrypt20-dev ,libcurl4-gnutls-dev - ,libgnutls-openssl-dev - ,libjpeg8-dev|libjpeg9-dev|libjpeg62-turbo-dev, - ,libmysqlclient-dev + ,libturbojpeg0-dev + ,default-libmysqlclient-dev | libmysqlclient-dev | libmariadbclient-dev-compat ,libpcre3-dev ,libpolkit-gobject-1-dev - ,libv4l-dev (>= 0.8.3) [!hurd-any] + ,libv4l-dev [!hurd-any] ,libvlc-dev ,libdate-manip-perl ,libdbd-mysql-perl ,libphp-serialization-perl ,libsys-mmap-perl [!hurd-any] ,libwww-perl - ,libdata-uuid-perl + ,libdata-uuid-perl ,libssl-dev ,libcrypt-eksblowfish-perl ,libdata-entropy-perl # Unbundled (dh_linktree): ,libjs-jquery ,libjs-mootools -Standards-Version: 3.9.4 +Standards-Version: 3.9.8 Homepage: http://www.zoneminder.com/ Vcs-Browser: http://anonscm.debian.org/cgit/collab-maint/zoneminder.git Vcs-Git: git://anonscm.debian.org/collab-maint/zoneminder.git @@ -38,16 +44,15 @@ Package: zoneminder Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,javascript-common - ,libmp4v2-2, libx264-142 - ,libav-tools|ffmpeg - ,libdate-manip-perl + ,libmp4v2-2, libx264-155 + ,libswscale5 + ,libswresample3 + ,ffmpeg + ,libdate-manip-perl, libmime-lite-perl, libmime-tools-perl ,libdbd-mysql-perl - ,libmime-lite-perl - ,libmime-tools-perl ,libphp-serialization-perl ,libmodule-load-conditional-perl ,libnet-sftp-foreign-perl -# ,libzoneminder-perl (= ${source:Version}) ,libarchive-zip-perl ,libdbd-mysql-perl ,libdevice-serialport-perl @@ -55,26 +60,31 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,libjson-maybexs-perl ,libsys-mmap-perl [!hurd-any] ,liburi-encode-perl - ,libwww-perl - ,libdata-uuid-perl + ,libwww-perl, liburi-perl + ,libdata-dump-perl + ,libdatetime-perl + ,libclass-std-fast-perl + ,libsoap-wsdl-perl + ,libio-socket-multicast-perl + ,libdigest-sha-perl + ,libsys-cpu-perl, libsys-meminfo-perl + ,libdata-uuid-perl ,libnumber-bytes-human-perl ,libfile-slurp-perl - ,mysql-client | virtual-mysql-client + ,mysql-client | mariadb-client | virtual-mysql-client ,perl-modules - ,php5-mysql, php5-gd, php5-apcu, php-apc + ,php-mysql, php-gd, php-apcu, php-apc | php-apcu-bc, php-json ,policykit-1 ,rsyslog | system-log-daemon ,zip - ,libdata-dump-perl, libclass-std-fast-perl, libsoap-wsdl-perl - ,libio-socket-multicast-perl, libdigest-sha-perl - ,libsys-cpu-perl, libsys-meminfo-perl - ,libssl | libssl1.0.0 + ,libpcre3 ,libcrypt-eksblowfish-perl ,libdata-entropy-perl Recommends: ${misc:Recommends} - ,libapache2-mod-php5 | php5-fpm - ,mysql-server | virtual-mysql-server + ,libapache2-mod-php | php-fpm + ,mysql-server | mariadb-server | virtual-mysql-server ,zoneminder-doc (>= ${source:Version}) + ,ffmpeg Suggests: fcgiwrap, logrotate Description: video camera security and surveillance solution ZoneMinder is intended for use in single or multi-camera video security @@ -120,7 +130,7 @@ Package: zoneminder-doc Section: doc Architecture: all Multi-Arch: foreign -Depends: ${misc:Depends}, ${sphinxdoc:Depends} +Depends: ${misc:Depends}, ${sphinxdoc:Depends}, python-sphinx-rtd-theme | python3-sphinx-rtd-theme Suggests: www-browser Description: ZoneMinder documentation ZoneMinder is intended for use in single or multi-camera video security diff --git a/distros/ubuntu1204/copyright b/distros/beowulf/copyright similarity index 100% rename from distros/ubuntu1204/copyright rename to distros/beowulf/copyright diff --git a/distros/ubuntu1204/examples/nginx.conf b/distros/beowulf/examples/nginx.conf similarity index 100% rename from distros/ubuntu1204/examples/nginx.conf rename to distros/beowulf/examples/nginx.conf diff --git a/distros/ubuntu1204/gbp.conf b/distros/beowulf/gbp.conf similarity index 100% rename from distros/ubuntu1204/gbp.conf rename to distros/beowulf/gbp.conf diff --git a/distros/ubuntu1204/libzoneminder-perl.install b/distros/beowulf/libzoneminder-perl.install similarity index 100% rename from distros/ubuntu1204/libzoneminder-perl.install rename to distros/beowulf/libzoneminder-perl.install diff --git a/distros/ubuntu1204/patches/series b/distros/beowulf/patches/series similarity index 100% rename from distros/ubuntu1204/patches/series rename to distros/beowulf/patches/series diff --git a/distros/ubuntu1204/rules b/distros/beowulf/rules similarity index 71% rename from distros/ubuntu1204/rules rename to distros/beowulf/rules index 657697fcf..e26f1bb91 100755 --- a/distros/ubuntu1204/rules +++ b/distros/beowulf/rules @@ -16,23 +16,23 @@ endif --with sphinxdoc,apache2,linktree override_dh_auto_configure: - dh_auto_configure -- $(ARGS) \ - -DCMAKE_VERBOSE_MAKEFILE=ON \ - -DCMAKE_BUILD_TYPE=Release \ - -DZM_CONFIG_DIR="/etc/zm" \ - -DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \ - -DZM_RUNDIR="/var/run/zm" \ - -DZM_SOCKDIR="/var/run/zm" \ - -DZM_TMPDIR="/tmp/zm" \ - -DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \ - -DZM_CACHEDIR="/var/cache/zoneminder/cache" \ + dh_auto_configure -- $(ARGS) \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DZM_CONFIG_DIR="/etc/zm" \ + -DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \ + -DZM_RUNDIR="/run/zm" \ + -DZM_SOCKDIR="/run/zm" \ + -DZM_TMPDIR="/tmp/zm" \ + -DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \ + -DZM_CACHEDIR="/var/cache/zoneminder/cache" \ -DZM_DIR_EVENTS="/var/cache/zoneminder/events" \ - -DZM_PATH_SHUTDOWN="/sbin/shutdown" \ - -DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" + -DZM_PATH_SHUTDOWN="/sbin/shutdown" \ + -DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" override_dh_clean: dh_clean $(MANPAGES1) - $(RM) -r docs/_build docs/installationguide + $(RM) -r docs/_build build-indep: #$(MAKE) -C docs text @@ -67,12 +67,6 @@ override_dh_fixperms: chown root:www-data $(CURDIR)/debian/zoneminder/etc/zm/zm.conf chmod 640 $(CURDIR)/debian/zoneminder/etc/zm/zm.conf -override_dh_installinit: - dh_installinit --no-start - -override_dh_apache2: - dh_apache2 --noenable - override_dh_strip: [ -d "$(CURDIR)/debian/zoneminder-dbg" ] \ && dh_strip --dbg-package=zoneminder-dbg \ diff --git a/distros/ubuntu1204/source/format b/distros/beowulf/source/format similarity index 100% rename from distros/ubuntu1204/source/format rename to distros/beowulf/source/format diff --git a/distros/ubuntu1204/source/lintian-overrides b/distros/beowulf/source/lintian-overrides similarity index 100% rename from distros/ubuntu1204/source/lintian-overrides rename to distros/beowulf/source/lintian-overrides diff --git a/distros/ubuntu1204/zoneminder-doc.doc-base b/distros/beowulf/zoneminder-doc.doc-base similarity index 100% rename from distros/ubuntu1204/zoneminder-doc.doc-base rename to distros/beowulf/zoneminder-doc.doc-base diff --git a/distros/ubuntu1204/zoneminder-doc.install b/distros/beowulf/zoneminder-doc.install similarity index 100% rename from distros/ubuntu1204/zoneminder-doc.install rename to distros/beowulf/zoneminder-doc.install diff --git a/distros/ubuntu1204/zoneminder-doc.links b/distros/beowulf/zoneminder-doc.links similarity index 100% rename from distros/ubuntu1204/zoneminder-doc.links rename to distros/beowulf/zoneminder-doc.links diff --git a/distros/ubuntu1204/zoneminder.apache2 b/distros/beowulf/zoneminder.apache2 similarity index 100% rename from distros/ubuntu1204/zoneminder.apache2 rename to distros/beowulf/zoneminder.apache2 diff --git a/distros/ubuntu1204/zoneminder.bug-presubj b/distros/beowulf/zoneminder.bug-presubj similarity index 100% rename from distros/ubuntu1204/zoneminder.bug-presubj rename to distros/beowulf/zoneminder.bug-presubj diff --git a/distros/ubuntu1204/zoneminder.dirs b/distros/beowulf/zoneminder.dirs similarity index 95% rename from distros/ubuntu1204/zoneminder.dirs rename to distros/beowulf/zoneminder.dirs index 79b2c66af..6db3d5a95 100644 --- a/distros/ubuntu1204/zoneminder.dirs +++ b/distros/beowulf/zoneminder.dirs @@ -5,5 +5,5 @@ var/cache/zoneminder/images var/cache/zoneminder/temp var/cache/zoneminder/cache usr/share/zoneminder/db -etc/zm +etc/zm/ etc/zm/conf.d diff --git a/distros/ubuntu1204/zoneminder.docs b/distros/beowulf/zoneminder.docs similarity index 100% rename from distros/ubuntu1204/zoneminder.docs rename to distros/beowulf/zoneminder.docs diff --git a/distros/ubuntu1204/zoneminder.examples b/distros/beowulf/zoneminder.examples similarity index 100% rename from distros/ubuntu1204/zoneminder.examples rename to distros/beowulf/zoneminder.examples diff --git a/distros/ubuntu1204/zoneminder.init b/distros/beowulf/zoneminder.init similarity index 86% rename from distros/ubuntu1204/zoneminder.init rename to distros/beowulf/zoneminder.init index de552848b..6132481f3 100644 --- a/distros/ubuntu1204/zoneminder.init +++ b/distros/beowulf/zoneminder.init @@ -17,19 +17,12 @@ prog=ZoneMinder ZM_PATH_BIN="/usr/bin" -RUNDIR="/var/run/zm" +RUNDIR="/run/zm" TMPDIR="/tmp/zm" command="$ZM_PATH_BIN/zmpkg.pl" start() { echo -n "Starting $prog: " - # Wait for mysqld to start. Continue if it takes too long. - count=0 - while [ ! -e /var/run/mysqld/mysqld.sock ] && [ $count -lt 60 ] - do - sleep 1 - count=$((count+1)) - done export TZ=:/etc/localtime mkdir -p "$RUNDIR" && chown www-data:www-data "$RUNDIR" mkdir -p "$TMPDIR" && chown www-data:www-data "$TMPDIR" diff --git a/distros/ubuntu1204/zoneminder.install b/distros/beowulf/zoneminder.install similarity index 100% rename from distros/ubuntu1204/zoneminder.install rename to distros/beowulf/zoneminder.install diff --git a/distros/beowulf/zoneminder.links b/distros/beowulf/zoneminder.links new file mode 100644 index 000000000..b7258c3c4 --- /dev/null +++ b/distros/beowulf/zoneminder.links @@ -0,0 +1 @@ +/var/tmp /usr/share/zoneminder/www/api/app/tmp diff --git a/distros/ubuntu1204/zoneminder.linktrees b/distros/beowulf/zoneminder.linktrees similarity index 100% rename from distros/ubuntu1204/zoneminder.linktrees rename to distros/beowulf/zoneminder.linktrees diff --git a/distros/ubuntu1204/zoneminder.lintian-overrides b/distros/beowulf/zoneminder.lintian-overrides similarity index 100% rename from distros/ubuntu1204/zoneminder.lintian-overrides rename to distros/beowulf/zoneminder.lintian-overrides diff --git a/distros/ubuntu1204/zoneminder.logrotate b/distros/beowulf/zoneminder.logrotate similarity index 85% rename from distros/ubuntu1204/zoneminder.logrotate rename to distros/beowulf/zoneminder.logrotate index 846abd4fb..6162e9c4d 100644 --- a/distros/ubuntu1204/zoneminder.logrotate +++ b/distros/beowulf/zoneminder.logrotate @@ -1,4 +1,4 @@ -/var/log/zm/*log { +/var/log/zm/*.log { missingok notifempty sharedscripts @@ -9,4 +9,5 @@ endscript daily rotate 7 + maxage 7 } diff --git a/distros/ubuntu1204/zoneminder.maintscript b/distros/beowulf/zoneminder.maintscript similarity index 100% rename from distros/ubuntu1204/zoneminder.maintscript rename to distros/beowulf/zoneminder.maintscript diff --git a/distros/ubuntu1204/zoneminder.manpages b/distros/beowulf/zoneminder.manpages similarity index 100% rename from distros/ubuntu1204/zoneminder.manpages rename to distros/beowulf/zoneminder.manpages diff --git a/distros/ubuntu1204/zoneminder.postinst b/distros/beowulf/zoneminder.postinst similarity index 100% rename from distros/ubuntu1204/zoneminder.postinst rename to distros/beowulf/zoneminder.postinst diff --git a/distros/ubuntu1204/zoneminder.postrm b/distros/beowulf/zoneminder.postrm similarity index 100% rename from distros/ubuntu1204/zoneminder.postrm rename to distros/beowulf/zoneminder.postrm diff --git a/distros/beowulf/zoneminder.preinst b/distros/beowulf/zoneminder.preinst new file mode 100644 index 000000000..6088c3ea9 --- /dev/null +++ b/distros/beowulf/zoneminder.preinst @@ -0,0 +1,11 @@ +#!/bin/sh + +set -e + +## Remove obsolete symlink which is in the way of dh_apache2: +ol="/etc/apache2/conf-available/zoneminder.conf" +if [ -h "${ol}" ]; then + [ "$(readlink ${ol})" = "/etc/zm/apache.conf" ] && rm -f "${ol}" +fi + +#DEBHELPER# diff --git a/distros/beowulf/zoneminder.tmpfile b/distros/beowulf/zoneminder.tmpfile new file mode 100644 index 000000000..cbfdec1de --- /dev/null +++ b/distros/beowulf/zoneminder.tmpfile @@ -0,0 +1,4 @@ +d /run/zm 0755 www-data www-data +d /tmp/zm 0755 www-data www-data +d /var/tmp/zm 0755 www-data www-data +d /var/cache/zoneminder/cache 0755 www-data www-data diff --git a/distros/debian/changelog b/distros/debian/changelog index 46f1e2bd9..72841f158 100644 --- a/distros/debian/changelog +++ b/distros/debian/changelog @@ -40,7 +40,7 @@ zoneminder (1.27.99+1-testing-SNAPSHOT2014072901) testing; urgency=medium -- Isaac Connor Tue, 29 Jul 2014 14:50:20 -0400 -zoneminder (1.27.0+1-testing-v4ltomonitor-1) testing; urgency=high +zoneminder (1.27.0+1-trusty-v4ltomonitor-1) trusty; urgency=high * Snapshot release - diff --git a/distros/redhat/CMakeLists.txt b/distros/redhat/CMakeLists.txt index f1a1bc75b..45cafe57d 100644 --- a/distros/redhat/CMakeLists.txt +++ b/distros/redhat/CMakeLists.txt @@ -21,7 +21,7 @@ endif(ZM_TARGET_DISTRO MATCHES "^el") # Configure the common zoneminder files configure_file(common/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY) configure_file(common/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY) -file(MAKE_DIRECTORY sock swap zoneminder zoneminder-upload events temp) +file(MAKE_DIRECTORY sock swap zoneminder events temp) # Configure the Apache zoneminder files configure_file(httpd/zm-httpd.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zm-httpd.conf @ONLY) @@ -51,7 +51,6 @@ install(DIRECTORY sock swap DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSION install(DIRECTORY zoneminder DESTINATION /var/log DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY zoneminder DESTINATION /var/run DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY zoneminder DESTINATION /var/cache DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) -install(DIRECTORY zoneminder-upload DESTINATION /var/spool DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY events temp DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) # Install the Apache zoneminder files 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/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..8a03de245 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -14,23 +14,32 @@ # 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.35.6 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons -# Mootools is inder the MIT license: http://mootools.net/ +# Mootools is under the MIT license: http://mootools.net/ +# jQuery is under the MIT license: https://jquery.org/license/ # CakePHP is under the MIT license: https://github.com/cakephp/cakephp # Crud is under the MIT license: https://github.com/FriendsOfCake/crud # CakePHP-Enum-Behavior is under the MIT license: https://github.com/asper/CakePHP-Enum-Behavior +# Bootstrap is under the MIT license: https://getbootstrap.com/docs/4.5/about/license/ +# Bootstrap-table is under the MIT license: https://bootstrap-table.com/docs/about/license/ +# font-awesome is under CC-BY license: https://fontawesome.com/license/free License: GPLv2+ and LGPLv2+ and MIT URL: http://www.zoneminder.com/ @@ -38,6 +47,7 @@ Source0: https://github.com/ZoneMinder/ZoneMinder/archive/%{version}.tar.gz#/zon Source1: https://github.com/ZoneMinder/crud/archive/v%{crud_version}.tar.gz#/crud-%{crud_version}.tar.gz Source2: https://github.com/ZoneMinder/CakePHP-Enum-Behavior/archive/%{ceb_version}.tar.gz#/cakephp-enum-behavior-%{ceb_version}.tar.gz +%{?rhel:BuildRequires: epel-rpm-macros} BuildRequires: systemd-devel BuildRequires: mariadb-devel BuildRequires: perl-podlators @@ -105,7 +115,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 @@ -194,23 +204,24 @@ mv -f CakePHP-Enum-Behavior-%{ceb_version} ./web/api/app/Plugin/CakePHP-Enum-Beh # Change the following default values ./utils/zmeditconfigdata.sh ZM_OPT_CAMBOZOLA yes -./utils/zmeditconfigdata.sh ZM_UPLOAD_FTP_LOC_DIR %{_localstatedir}/spool/zoneminder-upload ./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 +# See https://fedoraproject.org/wiki/LTOByDefault +%define _lto_cflags %{nil} + %cmake3 \ -DZM_WEB_USER="%{zmuid_final}" \ -DZM_WEB_GROUP="%{zmgid_final}" \ -DZM_TARGET_DISTRO="%{zmtargetdistro}" \ . -%make_build +%cmake3_build %install -%make_install +%cmake3_install desktop-file-install \ --dir %{buildroot}%{_datadir}/applications \ @@ -352,6 +363,7 @@ EOF %{_bindir}/zmtelemetry.pl %{_bindir}/zmx10.pl %{_bindir}/zmonvif-probe.pl +%{_bindir}/zmonvif-trigger.pl %{_bindir}/zmstats.pl %{_bindir}/zmrecover.pl @@ -384,7 +396,6 @@ EOF %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/temp %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/cache/zoneminder %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/log/zoneminder -%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/spool/zoneminder-upload %files nginx %config(noreplace) %attr(640,root,nginx) %{_sysconfdir}/zm/zm.conf @@ -408,29 +419,28 @@ EOF %dir %attr(755,nginx,nginx) %{_sharedstatedir}/zoneminder/temp %dir %attr(755,nginx,nginx) %{_localstatedir}/cache/zoneminder %dir %attr(755,nginx,nginx) %{_localstatedir}/log/zoneminder -%dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload %changelog -* Sun Aug 11 2019 Andrew Bauer - 1.33.14-1 -- Bump to 1.33.13 Development +* Tue Feb 04 2020 Andrew Bauer - 1.34.2-1 +- 1.34.2 Release -* Sun Jul 07 2019 Andrew Bauer - 1.33.12-1 -- Bump to 1.33.12 Development +* Fri Jan 31 2020 Andrew Bauer - 1.34.1-1 +- 1.34.1 Release -* Sun Jun 23 2019 Andrew Bauer - 1.33.9-1 -- Bump to 1.33.9 Development +* Sat Jan 18 2020 Andrew Bauer - 1.34.0-1 +- 1.34.0 Release -* Tue Apr 30 2019 Andrew Bauer - 1.33.8-1 -- Bump to 1.33.8 Development +* Tue Dec 17 2019 Leigh Scott - 1.32.3-5 +- Mass rebuild for x264 -* Sun Apr 07 2019 Andrew Bauer - 1.33.6-1 -- Bump to 1.33.6 Development +* Wed Aug 07 2019 Leigh Scott - 1.32.3-4 +- Rebuild for new ffmpeg version -* Sat Mar 30 2019 Andrew Bauer - 1.33.4-1 -- Bump to 1.33.4 Development +* Tue Mar 12 2019 Sérgio Basto - 1.32.3-3 +- Mass rebuild for x264 -* Tue Dec 11 2018 Andrew Bauer - 1.33.0-1 -- Bump to 1.33.0 Development +* Tue Mar 05 2019 RPM Fusion Release Engineering - 1.32.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild * Sat Dec 08 2018 Andrew Bauer - 1.32.3-1 - 1.32.3 Release diff --git a/distros/ubuntu1204/NEWS b/distros/ubuntu1204/NEWS deleted file mode 100644 index e69de29bb..000000000 diff --git a/distros/ubuntu1204/changelog b/distros/ubuntu1204/changelog deleted file mode 100644 index b19a3f228..000000000 --- a/distros/ubuntu1204/changelog +++ /dev/null @@ -1,8 +0,0 @@ -<<<<<<< HEAD -zoneminder (1.31.0-trusty) trusty; urgency=medium - - * placeholder - - -- Isaac Connor Fri, 13 May 2016 09:45:49 -0400 -======= ->>>>>>> master diff --git a/distros/ubuntu1204/watch b/distros/ubuntu1204/watch deleted file mode 100644 index 7ee690edb..000000000 --- a/distros/ubuntu1204/watch +++ /dev/null @@ -1,7 +0,0 @@ -version=3 - -opts=\ -repacksuffix=+dfsg,\ -dversionmangle=s{\+dfsg\d*}{},\ - https://github.com/ZoneMinder/ZoneMinder/releases \ - .*/ZoneMinder/archive/v(.*).tar.gz diff --git a/distros/ubuntu1204/zoneminder.links b/distros/ubuntu1204/zoneminder.links deleted file mode 100644 index 373548919..000000000 --- a/distros/ubuntu1204/zoneminder.links +++ /dev/null @@ -1 +0,0 @@ -/tmp/zm /usr/share/zoneminder/www/api/app/tmp diff --git a/distros/ubuntu1204/zoneminder.preinst b/distros/ubuntu1204/zoneminder.preinst deleted file mode 100644 index 3f75a1b3e..000000000 --- a/distros/ubuntu1204/zoneminder.preinst +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/sh - -set -e - -## Remove obsolete symlink which is in the way of dh_apache2: -ol="/etc/apache2/conf-available/zoneminder.conf" -if [ -h "${ol}" ]; then - [ "$(readlink ${ol})" = "/etc/zm/apache.conf" ] && rm -f "${ol}" -fi - -abort=false -if [ -h /usr/share/zoneminder/www/events ]; then - l=$(readlink /usr/share/zoneminder/www/events) - if [ "$l" != "/var/cache/zoneminder/events" ]; then - abort=true - fi -fi -if [ -h /usr/share/zoneminder/www/images ]; then - l=$(readlink /usr/share/zoneminder/www/images ) - if [ "$l" != "/var/cache/zoneminder/images" ]; then - abort=true - fi -fi - -if [ "$abort" = "true" ]; then - cat >&2 << EOF -Aborting installation of zoneminder due to non-default symlinks in -/usr/share/zoneminder for the images and/or events directory, which could -result in loss of data. Please move your data in each of these directories to -/var/cache/zoneminder before installing zoneminder from the package. -EOF - exit 1 - -fi - -#DEBHELPER# diff --git a/distros/ubuntu1204/zoneminder.tmpfile b/distros/ubuntu1204/zoneminder.tmpfile deleted file mode 100644 index 017955900..000000000 --- a/distros/ubuntu1204/zoneminder.tmpfile +++ /dev/null @@ -1,3 +0,0 @@ -d /var/run/zm 0755 www-data www-data -d /tmp/zm 0755 www-data www-data -d /var/tmp/zm 0755 www-data www-data diff --git a/distros/ubuntu1410/README.Debian b/distros/ubuntu1410/README.Debian deleted file mode 100644 index a49b6be72..000000000 --- a/distros/ubuntu1410/README.Debian +++ /dev/null @@ -1,51 +0,0 @@ -zoneminder for Debian ---------------------- - -There is one manual step to get the web interface working. -You need to link /etc/zm/apache.conf to /etc/apache2/conf.d/zoneminder.conf, -then reload the apache config (i.e. /etc/init.d/apache2 reload) - -Changing the location for images and events -------------------------------------------- - -Zoneminder, in its upstream form, stores data in /usr/share/zoneminder/. This -package modifies that by changing /usr/share/zoneminder/images and -/usr/share/zoneminder/events to symlinks to directories under -/var/cache/zoneminder. - -There are numerous places these could be put and ways to do it. But, at the -moment, if you change this, an upgrade will fail with a warning about these -locations having changed (the reason for this was that previously, an upgrade -would silently revert the changes and cause event loss - refer -bug #608793). - -If you do want to change the location, here are a couple of suggestions. - -These lines would mount /dev/sdX1 to /video_storage, and then 'link' /video_storage -to the locations that ZoneMinder expects them to be at. - - /dev/sdX1 /video_storage ext4 defaults 0 2 - /video_storage/zoneminder/images /var/cache/zoneminder/images none bind 0 2 - /video_storage/zoneminder/events /var/cache/zoneminder/events none bind 0 2 - - or if you have a separate partition for each: - - /dev/sdX1 /var/cache/zoneminder/images ext4 defaults 0 2 - /dev/sdX2 /var/cache/zoneminder/events ext4 defaults 0 2 - - - - -- Peter Howard , Sun, 16 Jan 2010 01:35:51 +1100 - -Access to /dev/video* ---------------------- - -For cameras which require access to /dev/video*, zoneminder may need the -www-data user added to the video group in order to see those cameras: - - adduser www-data video - -Note that all web applications running on the zoneminder server will then have -access to all video devices on the system. - - -- Vagrant Cascadian Sun, 27 Mar 2011 13:06:56 -0700 diff --git a/distros/ubuntu1410/apache.conf b/distros/ubuntu1410/apache.conf deleted file mode 100644 index 92a2b6414..000000000 --- a/distros/ubuntu1410/apache.conf +++ /dev/null @@ -1,9 +0,0 @@ -Alias /zm /usr/share/zoneminder - - - php_flag register_globals off - Options Indexes FollowSymLinks - - DirectoryIndex index.php - - diff --git a/distros/ubuntu1410/changelog b/distros/ubuntu1410/changelog deleted file mode 100644 index 55e3d17b0..000000000 --- a/distros/ubuntu1410/changelog +++ /dev/null @@ -1,323 +0,0 @@ -zoneminder (1.30.2-trusty-2016033001) trusty; urgency=medium - - * merge master - - -- Isaac Connor Wed, 30 Mar 2016 14:09:48 -0400 - -zoneminder (1.30.2-trusty-2016032901) trusty; urgency=medium - - * filter fixes, merge options rework by Kyle - - -- Isaac Connor Tue, 29 Mar 2016 12:27:57 -0400 - -zoneminder (1.30.2-trusty-2016030702) trusty; urgency=medium - - * - - -- Isaac Connor Mon, 07 Mar 2016 22:14:03 -0500 - -zoneminder (1.30.2-trusty-2016030701) trusty; urgency=medium - - * merge master. with telemetry - - -- Isaac Connor Mon, 07 Mar 2016 21:47:53 -0500 - -zoneminder (1.30.2-trusty-2016022101) trusty; urgency=medium - - * merge zmtrigger fix - - -- Isaac Connor Mon, 22 Feb 2016 09:15:53 -0500 - -zoneminder (1.30.2-trusty-2016021901) trusty; urgency=medium - - * zmtrigger improvements - - -- Isaac Connor Fri, 19 Feb 2016 11:09:57 -0500 - -zoneminder (1.30.2-trusty-2016021701) trusty; urgency=medium - - * printout id, and ip address when failing to connect - - -- Isaac Connor Wed, 17 Feb 2016 09:40:49 -0500 - -zoneminder (1.30.2-trusty-2016021001) trusty; urgency=medium - - * - - -- Isaac Connor Wed, 10 Feb 2016 13:06:09 -0500 - -zoneminder (1.29.111-trusty-2016020101) trusty; urgency=medium - - * Fix video download and use of Storage Areas - - -- Isaac Connor Mon, 01 Feb 2016 13:42:06 -0500 - -zoneminder (1.29.111-trusty-2016011401) trusty; urgency=medium - - * fix timeline view for storageareas - - -- Isaac Connor Thu, 14 Jan 2016 14:03:41 -0500 - -zoneminder (1.29.111-trusty-2016010801) trusty; urgency=medium - - * Add better debug and skip when event links are not just digits. Merge multi-server stuff from master. - - -- Isaac Connor Fri, 08 Jan 2016 10:37:16 -0500 - -zoneminder (1.29.111-trusty-2016010401) trusty; urgency=medium - - * include fix to rotate image dimensions when applying a rotation - - -- Isaac Connor Mon, 04 Jan 2016 13:24:42 -0500 - -zoneminder (1.29.111-trusty-2016010101) trusty; urgency=medium - - * fix logging with multi-server - - -- Isaac Connor Fri, 01 Jan 2016 17:11:09 -0500 - -zoneminder (1.29.111-trusty-2015123101) trusty; urgency=medium - - * Add log filtering from multi-server - - -- Isaac Connor Thu, 31 Dec 2015 10:18:03 -0500 - -zoneminder (1.29.109-trusty-2015122401) trusty; urgency=medium - - * fix delete events not in database in zmaudit.pl - - -- Isaac Connor Thu, 24 Dec 2015 12:38:05 -0500 - -zoneminder (1.29.109-trusty-2015122301) trusty; urgency=medium - - * todays release - - -- Isaac Connor Wed, 23 Dec 2015 09:33:46 -0500 - -zoneminder (1.29.109-trusty-2015122202) trusty; urgency=medium - - * more object work and zmaudit - - -- Isaac Connor Tue, 22 Dec 2015 13:13:56 -0500 - -zoneminder (1.29.109-trusty-2015122201) trusty; urgency=medium - - * merge multi-server, and master stuff, start work on zmaudit storage areas support - - -- Isaac Connor Tue, 22 Dec 2015 11:11:44 -0500 - -zoneminder (1.29.109-trusty-2015122103) trusty; urgency=medium - - * Fixes - - -- Isaac Connor Mon, 21 Dec 2015 15:20:15 -0500 - -zoneminder (1.29.109-trusty-2015122102) trusty; urgency=medium - - * fix deleting events - - -- Isaac Connor Mon, 21 Dec 2015 14:49:12 -0500 - -zoneminder (1.29.109-trusty-2015122101) trusty; urgency=medium - - * Make zmfilter work. - - -- Isaac Connor Mon, 21 Dec 2015 12:32:27 -0500 - -zoneminder (1.29.109-trusty-2015121803) trusty; urgency=medium - - * merge zmvideo improvements - - -- Isaac Connor Fri, 18 Dec 2015 14:17:56 -0500 - -zoneminder (1.29.109-trusty-2015121802) trusty; urgency=medium - - * Add some missing files to the autoconf build - - -- Isaac Connor Fri, 18 Dec 2015 12:14:08 -0500 - -zoneminder (1.29.109-trusty-2015121801) trusty; urgency=medium - - * - - -- Isaac Connor Fri, 18 Dec 2015 11:05:58 -0500 - -zoneminder (1.29.109-trusty-2015121701) trusty; urgency=medium - - * Merge master + better zmvideo - - -- Isaac Connor Thu, 17 Dec 2015 15:11:18 -0500 - -zoneminder (1.29.0-trusty-2015112301) trusty; urgency=medium - - * apply fix for zms crash - - -- Isaac Connor Mon, 23 Nov 2015 10:47:49 -0500 - -zoneminder (1.29.0-trusty-2015110601) trusty; urgency=medium - - * add a FIONREAD test on timeout - - -- Isaac Connor Wed, 22 Jul 2015 09:55:37 -0400 - -zoneminder (1.29.0-trusty-2015072201) trusty; urgency=medium - - * add AnalysisFPS - - -- Isaac Connor Wed, 22 Jul 2015 09:55:37 -0400 - -zoneminder (1.29.0-trusty-2015071601) trusty; urgency=medium - - * Merge master and zmtrigger - - -- Isaac Connor Thu, 16 Jul 2015 13:15:58 -0400 - -zoneminder (1.29.0-trusty-38) trusty; urgency=medium - - * Merge master - - -- Isaac Connor Tue, 14 Jul 2015 10:15:00 -0400 - -zoneminder (1.29.0-trusty-37) trusty; urgency=medium - - * merge master api stuff, set sleep after failure to capture to 5000 instead of 5000000 - - -- Isaac Connor Fri, 19 Jun 2015 09:59:54 -0400 - -zoneminder (1.29.0-trusty-36) trusty; urgency=medium - - * Detect select interuption when no action on our fd - - -- Isaac Connor Thu, 28 May 2015 09:35:59 -0400 - -zoneminder (1.29.0-trusty-35) trusty; urgency=medium - - * better logging - - -- Isaac Connor Fri, 22 May 2015 15:30:42 -0400 - -zoneminder (1.29.0-trusty-34) trusty; urgency=medium - - * Faster shutdown and changes to ReadData - - -- Isaac Connor Thu, 21 May 2015 15:54:47 -0400 - -zoneminder (1.29.0-trusty-33) trusty; urgency=medium - - * update zmaudit some more - - -- Isaac Connor Thu, 14 May 2015 13:44:35 -0400 - -zoneminder (1.29.0-trusty-32) trusty; urgency=medium - - * merge zmaudit_updates1 - - -- Isaac Connor Wed, 13 May 2015 15:51:56 -0400 - -zoneminder (1.29.0-trusty-31) trusty; urgency=medium - - * Merge some fixes and zmaudit improvements - - -- Isaac Connor Wed, 13 May 2015 15:16:19 -0400 - -zoneminder (1.29.0-trusty-27) trusty; urgency=medium - - * fflush logs, merge master. - - -- Isaac Connor Mon, 30 Mar 2015 19:28:05 -0400 - -zoneminder (1.29.0-vivid-26) vivid; urgency=medium - - * logging improvements, merge RedData changes - - -- Isaac Connor Wed, 04 Mar 2015 16:39:19 -0500 - -zoneminder (1.29.0-vivid-25) vivid; urgency=medium - - * some change to ReadData - - -- Isaac Connor Mon, 02 Mar 2015 12:57:16 -0500 - -zoneminder (1.29.0-utopic-24) utopic; urgency=medium - - * merge local_raw - - -- Isaac Connor Mon, 23 Feb 2015 17:49:36 -0500 - -zoneminder (1.29.0-trusty-23) trusty; urgency=medium - - * more onvif merges, fix to zmfilter - - -- Isaac Connor Sat, 21 Feb 2015 16:17:01 -0500 - -zoneminder (1.29.0-trusty-22) trusty; urgency=medium - - * updates from master: merge onvif, default to classic if skin not defined. - - -- Isaac Connor Thu, 19 Feb 2015 18:14:58 -0500 - -zoneminder (1.29.0-utopic-21) utopic; urgency=medium - - * updates from master, improve monitor probing and support TRENDnet cameras - - -- Isaac Connor Tue, 17 Feb 2015 14:18:52 -0500 - -zoneminder (1.29.0-wheezy-20) wheezy; urgency=medium - - * zmaudit.pl improvements - double check db before deleting fs event - - -- Isaac Connor Wed, 04 Feb 2015 11:09:22 -0500 - -zoneminder (1.29.0-utopic-18) utopic; urgency=medium - - * RTSP Timeout fixes - - -- Isaac Connor Wed, 28 Jan 2015 13:49:16 -0500 - -zoneminder (1.29.0-utopic-17) utopic; urgency=medium - - * Merge master, use new split-up debian build - - -- Isaac Connor Mon, 26 Jan 2015 11:21:07 -0500 - -zoneminder (1.28.0+nmu1) testing; urgency=medium - - * Non-maintainer upload - * Split the debian package into several packages - * Switch to native source format - - -- Emmanuel Papin Thu, 15 Jan 2015 20:00:08 +0100 - -zoneminder (1.28.0-0.2) testing; urgency=medium - - * Non-maintainer upload. - * Upstream release for debian jessie - * Package dependencies updated - * debhelper version upgraded - * Standards-Version upgraded - * Use debhelper commands instead of standard commands - * Install man pages in /usr/share/man (patch added) - * Switch to quilt - * Switch to systemd - * Some lintian fixes - - -- Emmanuel Papin Wed, 26 Nov 2014 00:26:01 +0100 - -zoneminder (1.28.0-0.1) stable; urgency=medium - - * Release - - -- Isaac Connor Fri, 17 Oct 2014 09:27:22 -0400 - -zoneminder (1.27.99+1-testing-SNAPSHOT2014072901) testing; urgency=medium - - * improve error messages - * Make zmupdate re-run the most recent patch so that people running the daily builds get their db updates - - -- Isaac Connor Tue, 29 Jul 2014 14:50:20 -0400 - -zoneminder (1.27.0+1-testing-v4ltomonitor-1) testing; urgency=high - - * Snapshot release - - - -- Isaac Connor Wed, 09 Jul 2014 21:35:29 -0400 diff --git a/distros/ubuntu1410/control b/distros/ubuntu1410/control deleted file mode 100644 index 5a873f133..000000000 --- a/distros/ubuntu1410/control +++ /dev/null @@ -1,122 +0,0 @@ -Source: zoneminder -Section: net -Priority: optional -Maintainer: Isaac Connor -Build-Depends: debhelper (>= 9), po-debconf (>= 1.0), autoconf, automake, libphp-serialization-perl, libgnutls-dev, libmysqlclient-dev | libmariadbclient-dev, libdbd-mysql-perl, libdate-manip-perl, libwww-perl, libjpeg8-dev | libjpeg9-dev | libjpeg62-turbo-dev, libpcre3-dev, libavcodec-dev, libavformat-dev (>= 3:0.svn20090204), libswscale-dev (>= 3:0.svn20090204), libavutil-dev, libv4l-dev (>= 0.8.3), libbz2-dev, libtool, libsys-mmap-perl, libavdevice-dev, libdevice-serialport-perl, libarchive-zip-perl, libmime-lite-perl, dh-autoreconf, libvlccore-dev, libvlc-dev, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev, libgcrypt11-dev | libgcrypt20-dev, libpolkit-gobject-1-dev, libdbi-perl, libnet-sftp-foreign-perl, libexpect-perl, libmime-tools-perl, libx264-dev, libmp4v2-dev, libpcre3-dev -Standards-Version: 3.9.6 - -Package: zoneminder -Section: metapackages -Architecture: all -Depends: ${misc:Depends}, - libzoneminder-perl (>= ${source:Version}), - zoneminder-database (>= ${source:Version}), - zoneminder-core (>= ${binary:Version}), - zoneminder-ui-base (>= ${source:Version}), - zoneminder-ui-classic (>= ${source:Version}), - zoneminder-ui-mobile (>= ${source:Version}), - zoneminder-ui-xml (>= ${source:Version}) -Description: Video camera security and surveillance solution (metapackage) - ZoneMinder is intended for use in single or multi-camera video security - applications, including commercial or home CCTV, theft prevention and child - or family member or home monitoring and other care scenarios. It - supports capture, analysis, recording, and monitoring of video data coming - from one or more video or network cameras attached to a Linux system. - ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom - cameras using a variety of protocols. It is suitable for use as a home - video security system and for commercial or professional video security - and surveillance. It can also be integrated into a home automation system - via X.10 or other protocols. - -Package: libzoneminder-perl -Section: perl -Architecture: all -Depends: ${misc:Depends}, ${perl:Depends}, libdbi-perl, - libdevice-serialport-perl, libimage-info-perl, libjson-any-perl, libjson-maybexs-perl, - libsys-mmap-perl, liburi-encode-perl, libwww-perl -Description: Perl libraries for ZoneMinder - ZoneMinder is a video camera security and surveillance solution. - . - This package provides the libraries for the perl scripts, it can be used to - write custom interfaces as well. - -Package: zoneminder-database -Section: database -Architecture: all -Depends: ${misc:Depends}, debconf, dbconfig-common, - mysql-client | mariadb-client -Recommends: mysql-server | mariadb-server -Description: Database management package for ZoneMinder - ZoneMinder is a video camera security and surveillance solution. - . - This package provides the sql files and maintenance scripts to perform all the - database operations (installation, upgrade or removal) on a local or a remote - server. - -Package: zoneminder-core -Section: video -Architecture: any -Depends: libzoneminder-perl (= ${source:Version}), - zoneminder-database (= ${source:Version}), ${shlibs:Depends}, ${misc:Depends}, - ${perl:Depends}, libarchive-tar-perl, libarchive-zip-perl, libdate-manip-perl, - libdbi-perl, libmodule-load-conditional-perl, libmime-lite-perl, - libmime-tools-perl, libnet-sftp-foreign-perl, libphp-serialization-perl, - debconf, ffmpeg | libav-tools, rsyslog | system-log-daemon, zip, - policykit-1, apache2, libmp4v2-2, libpcre++0 -Description: Core binaries and perl scripts for ZoneMinder - ZoneMinder is a video camera security and surveillance solution. - . - This package provides the executable compiled binaries which do the main video - processing work and the perl scripts which perform helper and/or external - interface tasks. - -Package: zoneminder-core-dbg -Priority: extra -Section: debug -Architecture: any -Depends: zoneminder-core (= ${binary:Version}), ${misc:Depends} -Description: Debugging symbols for ZoneMinder - ZoneMinder is a video camera security and surveillance solution. - . - This package provides the debugging symbols for the executable compiled - binaries. - -Package: zoneminder-ui-base -Section: web -Architecture: any -Depends: zoneminder-core (= ${binary:Version}), ${shlibs:Depends}, - ${misc:Depends}, debconf, apache2, libapache2-mod-php5 | libapache2-mod-fcgid, - php5, php5-mysql | php5-mysqlnd, php5-gd -Description: Essential files for ZoneMinder's web user interface - ZoneMinder is a video camera security and surveillance solution. - . - This package provides the essential web files and maintenance scripts to set up - a basic web environment. - -Package: zoneminder-ui-classic -Section: web -Architecture: all -Depends: zoneminder-ui-base (>= ${source:Version}), ${misc:Depends} -Description: Classic web user interface for ZoneMinder - ZoneMinder is a video camera security and surveillance solution. - . - This package provides the classic web user interface. - -Package: zoneminder-ui-mobile -Section: web -Architecture: all -Depends: zoneminder-ui-base (>= ${source:Version}), ${misc:Depends} -Description: Mobile web user interface for ZoneMinder - ZoneMinder is a video camera security and surveillance solution. - . - This package provides the web user interface for mobile devices. - -Package: zoneminder-ui-xml -Section: web -Architecture: all -Depends: zoneminder-ui-base (>= ${source:Version}), ${misc:Depends} -Description: XML interface for ZoneMinder - ZoneMinder is a video camera security and surveillance solution. - . - This package provides a XML interface mainly intended for use with the eyeZm - iPhone Application, but can be used with any other custom programs as well. diff --git a/distros/ubuntu1410/copyright b/distros/ubuntu1410/copyright deleted file mode 100644 index a177502a0..000000000 --- a/distros/ubuntu1410/copyright +++ /dev/null @@ -1,22 +0,0 @@ -Copyright: - -Copyright 2002 Philip Coombes - -License: - -This package 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 package 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 package; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -On Debian GNU/Linux systems, the text of the GPL can be found in -/usr/share/common-licenses/GPL. diff --git a/distros/ubuntu1410/libzoneminder-perl.install b/distros/ubuntu1410/libzoneminder-perl.install deleted file mode 100644 index 792ffc15e..000000000 --- a/distros/ubuntu1410/libzoneminder-perl.install +++ /dev/null @@ -1,4 +0,0 @@ -usr/share/perl5/ZoneMinder -usr/share/perl5/ZoneMinder.pm -debian/tmp/usr/share/man/man3/ZoneMinder.3pm -debian/tmp/usr/share/man/man3/ZoneMinder::* diff --git a/distros/ubuntu1410/po/POTFILES.in b/distros/ubuntu1410/po/POTFILES.in deleted file mode 100644 index 5b155907e..000000000 --- a/distros/ubuntu1410/po/POTFILES.in +++ /dev/null @@ -1,3 +0,0 @@ -[type: gettext/rfc822deb] zoneminder-core.templates -[type: gettext/rfc822deb] zoneminder-database.templates -[type: gettext/rfc822deb] zoneminder-ui-base.templates diff --git a/distros/ubuntu1410/po/fr.po b/distros/ubuntu1410/po/fr.po deleted file mode 100644 index 85ced7fd2..000000000 --- a/distros/ubuntu1410/po/fr.po +++ /dev/null @@ -1,252 +0,0 @@ -# debconf french translation file for ZoneMinder. -# Copyright (C) 2001-2008 Philip Coombes -# This file is distributed under the same license as the zoneminder package. -# First author: Emmanuel Papin , 2014. -# -msgid "" -msgstr "" -"Project-Id-Version: zoneminder\n" -"Report-Msgid-Bugs-To: zoneminder@packages.debian.org\n" -"POT-Creation-Date: 2014-12-16 12:34+0100\n" -"PO-Revision-Date: 2014-12-07 00:40+0100\n" -"Last-Translator: Emmanuel Papin \n" -"Language-Team: French \n" -"Language: fr\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "Delete this non empty directory?" -msgstr "Supprimer ce répertoire non vide ?" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "" -"A purge of the ZoneMinder package is performed but the directory '/var/cache/" -"zoneminder' is not empty so it will not be deleted." -msgstr "" -"Une purge du paquet ZoneMinder est en cours mais le répertoire '/var/cache/" -"zoneminder' n'est pas vide et sera donc conservé." - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "" -"Please consider that this directory is designed to contain data resulting " -"from event detection. Therefore, \"proof of evidence\" could be lost!\"" -msgstr "" -"Veuillez considérer que ce répertoire est conçu pour contenir des données " -"résultants de la détection d'événements. Par conséquent, des preuves " -"pourraient être perdues !" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "" -"If you are not sure of your decision, please do not delete this directory " -"but perform a manual checkup." -msgstr "" -"Si vous n'êtes pas sûr de votre décision, veuillez conserver ce répertoire " -"et effectuer une vérification manuelle." - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:2001 -msgid "Deletion confirmed?" -msgstr "Supression confirmée ?" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:2001 -msgid "" -"You have allowed the deletion of directory '/var/cache/zoneminder' although " -"it may contain critical data." -msgstr "" -"Vous avez autorisé la suppression du répertoire '/var/cache/zoneminder' bien " -"qu'il puisse contenir des données critiques." - -#. Type: select -#. Choices -#: ../zoneminder-database.templates:1001 -msgid "local" -msgstr "local" - -#. Type: select -#. Choices -#: ../zoneminder-database.templates:1001 -msgid "remote" -msgstr "distant" - -#. Type: select -#. Description -#: ../zoneminder-database.templates:1002 -msgid "Database location:" -msgstr "Emplacement de la base de donnée :" - -#. Type: select -#. Description -#: ../zoneminder-database.templates:1002 -msgid "" -"A database server is required to run ZoneMinder. The database can be " -"installed either locally or remotely on a machine of your network." -msgstr "" -"Un serveur de base de données est requis pour ZoneMinder. La base de donnée " -"peut être installée localement ou à distance sur une machine de votre réseau." - -#. Type: select -#. Description -#: ../zoneminder-database.templates:1002 -msgid "" -"If you choose a remote location, you will have to select the 'tcp/ip' " -"connection method and enter the hostname or ip address of the remote machine " -"in the next configuration screens." -msgstr "" -"Si vous choisissez un emplacement distant, vous devrez sélectionner la " -"méthode de connexion 'tcp/ip' et entrer le nom réseau ou l'adresse ip de la " -"machine distante dans les écrans de configuration suivants." - -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 -msgid "No local database server is available:" -msgstr "Aucun serveur local de base de données n'est disponible :" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 -msgid "" -"Currently ZoneMinder supports mysql or mariadb database server but none of " -"them appears to be installed on this machine." -msgstr "" -"Actuellement ZoneMinder supporte les serveurs de base de données mysql et " -"mariadb mais aucun d'entre eux n'est installé sur cette machine." - -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 -msgid "" -"In order to complete ZoneMinder's installation, after ending of this " -"assistant, please install a compatible database server and then restart the " -"assistant by invoking:" -msgstr "" -"Afin de compléter l'installation de ZoneMinder, après la fermeture de cet " -"assitant, veuillez installer un serveur de base de données compatible et " -"ensuite redémarrez l'assistant en invoquant :" - -#. Type: error -#. Description -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 ../zoneminder-database.templates:3001 -msgid "$ sudo dpkg-reconfigure zoneminder" -msgstr "$ sudo dpkg-reconfigure zoneminder" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "Remote database servers are not allowed:" -msgstr "Les serveurs de base de données distants ne sont pas autorisés :" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "" -"The current configuration of dbconfig-common does not allow installation of " -"a database on remote servers." -msgstr "" -"La configuration actuelle de dbconfig-common ne permet pas l'installation de " -"bases de données sur des serveurs distants." - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "" -"In order to reconfigure dbconfig-common, please invoke the following command " -"after ending of this assistant:" -msgstr "" -"Afin de reconfigurer dbconfig-common, veuillez invoquer la commande suivante " -"après la fermeture de cet assitant :" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "$ sudo dpkg-reconfigure dbconfig-common" -msgstr "$ sudo dpkg-reconfigure dbconfig-common" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "" -"Then, to complete ZoneMinder's installation, please restart this assistant " -"by invoking:" -msgstr "" -"Ensuite, pour compléter l'installation de ZoneMinder, veuillez redémarrer " -"cet assistant en invoquant :" - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "New password for the ZoneMinder 'admin' user:" -msgstr "Nouveau mot de passe pour le compte 'admin' de ZoneMinder :" - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "Please enter the password of the default administrative user." -msgstr "Veuillez entrer le mot de passe du compte administrateur par défaut." - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "" -"While not mandatory, it is highly recommended that you set a custom password " -"for the administrative 'admin' user." -msgstr "" -"Bien que cela ne soit pas obligatoire, il est fortement recommandé de " -"fournir un mot de passe personnalisé pour le compte administrateur 'admin'." - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "If this field is left blank, the password will not be changed." -msgstr "Si le champ est laissé vide, le mot de passe ne sera pas changé." - -#. Type: password -#. Description -#: ../zoneminder-database.templates:5001 -msgid "Repeat password for the ZoneMinder 'admin' user:" -msgstr "Répéter le mot de passe pour le compte 'admin' de ZoneMinder :" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:6001 -msgid "Password input error" -msgstr "Erreur de mot de passe" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:6001 -msgid "The two passwords you entered were not the same. Please try again." -msgstr "" -"Les deux mots de passe saisis ne sont pas les mêmes. Veuillez essayer à " -"nouveau." - -#. Type: multiselect -#. Description -#: ../zoneminder-ui-base.templates:1001 -msgid "Web server to reconfigure automatically:" -msgstr "Serveur web à reconfigurer automatiquement :" - -#. Type: multiselect -#. Description -#: ../zoneminder-ui-base.templates:1001 -msgid "" -"Please choose the web server that should be automatically configured for " -"ZoneMinder's web portal access." -msgstr "" -"Veuillez choisir le serveur web à reconfigurer automatiquement pour l'accès " -"au portail web de ZoneMinder." diff --git a/distros/ubuntu1410/po/templates.pot b/distros/ubuntu1410/po/templates.pot deleted file mode 100644 index 941a4094e..000000000 --- a/distros/ubuntu1410/po/templates.pot +++ /dev/null @@ -1,222 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: zoneminder\n" -"Report-Msgid-Bugs-To: zoneminder@packages.debian.org\n" -"POT-Creation-Date: 2014-12-16 12:34+0100\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" -"Content-Transfer-Encoding: 8bit\n" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "Delete this non empty directory?" -msgstr "" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "" -"A purge of the ZoneMinder package is performed but the directory '/var/cache/" -"zoneminder' is not empty so it will not be deleted." -msgstr "" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "" -"Please consider that this directory is designed to contain data resulting " -"from event detection. Therefore, \"proof of evidence\" could be lost!\"" -msgstr "" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "" -"If you are not sure of your decision, please do not delete this directory " -"but perform a manual checkup." -msgstr "" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:2001 -msgid "Deletion confirmed?" -msgstr "" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:2001 -msgid "" -"You have allowed the deletion of directory '/var/cache/zoneminder' although " -"it may contain critical data." -msgstr "" - -#. Type: select -#. Choices -#: ../zoneminder-database.templates:1001 -msgid "local" -msgstr "" - -#. Type: select -#. Choices -#: ../zoneminder-database.templates:1001 -msgid "remote" -msgstr "" - -#. Type: select -#. Description -#: ../zoneminder-database.templates:1002 -msgid "Database location:" -msgstr "" - -#. Type: select -#. Description -#: ../zoneminder-database.templates:1002 -msgid "" -"A database server is required to run ZoneMinder. The database can be " -"installed either locally or remotely on a machine of your network." -msgstr "" - -#. Type: select -#. Description -#: ../zoneminder-database.templates:1002 -msgid "" -"If you choose a remote location, you will have to select the 'tcp/ip' " -"connection method and enter the hostname or ip address of the remote machine " -"in the next configuration screens." -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 -msgid "No local database server is available:" -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 -msgid "" -"Currently ZoneMinder supports mysql or mariadb database server but none of " -"them appears to be installed on this machine." -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 -msgid "" -"In order to complete ZoneMinder's installation, after ending of this " -"assistant, please install a compatible database server and then restart the " -"assistant by invoking:" -msgstr "" - -#. Type: error -#. Description -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 ../zoneminder-database.templates:3001 -msgid "$ sudo dpkg-reconfigure zoneminder" -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "Remote database servers are not allowed:" -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "" -"The current configuration of dbconfig-common does not allow installation of " -"a database on remote servers." -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "" -"In order to reconfigure dbconfig-common, please invoke the following command " -"after ending of this assistant:" -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "$ sudo dpkg-reconfigure dbconfig-common" -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "" -"Then, to complete ZoneMinder's installation, please restart this assistant " -"by invoking:" -msgstr "" - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "New password for the ZoneMinder 'admin' user:" -msgstr "" - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "Please enter the password of the default administrative user." -msgstr "" - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "" -"While not mandatory, it is highly recommended that you set a custom password " -"for the administrative 'admin' user." -msgstr "" - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "If this field is left blank, the password will not be changed." -msgstr "" - -#. Type: password -#. Description -#: ../zoneminder-database.templates:5001 -msgid "Repeat password for the ZoneMinder 'admin' user:" -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:6001 -msgid "Password input error" -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:6001 -msgid "The two passwords you entered were not the same. Please try again." -msgstr "" - -#. Type: multiselect -#. Description -#: ../zoneminder-ui-base.templates:1001 -msgid "Web server to reconfigure automatically:" -msgstr "" - -#. Type: multiselect -#. Description -#: ../zoneminder-ui-base.templates:1001 -msgid "" -"Please choose the web server that should be automatically configured for " -"ZoneMinder's web portal access." -msgstr "" diff --git a/distros/ubuntu1410/rules b/distros/ubuntu1410/rules deleted file mode 100755 index 49d3549f1..000000000 --- a/distros/ubuntu1410/rules +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/make -f -# -*- makefile -*- -# Sample debian/rules that uses debhelper. -# This file was originally written by Joey Hess and Craig Small. -# As a special exception, when this file is copied by dh-make into a -# dh-make output file, you may use that output file without restriction. -# This special exception was added by Craig Small in version 0.37 of dh-make. - -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -# These are used for cross-compiling and for saving the configure script -# from having to guess our platform (since we know it already) -DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) -DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) - -CFLAGS = -Wall -CPPFLAGS = -D__STDC_CONSTANT_MACROS -CXXFLAGS = -DHAVE_LIBCRYPTO - -ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS))) -DEBOPT = --enable-debug -CFLAGS += -g -CXXFLAGS += -g -else -DEBOPT = -endif - -ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) -CFLAGS += -O0 -else -CFLAGS += -O2 -endif - -# These are used to get the most recent version of the original sources from github -UURL = $(shell git config --get remote.origin.url) -BRANCH = $(shell git rev-parse --abbrev-ref HEAD) -HEAD = $(shell git rev-parse HEAD) -PKD = $(abspath $(dir $(MAKEFILE_LIST))) -PKG = $(word 2,$(shell dpkg-parsechangelog -l$(PKD)/changelog | grep ^Source)) -VER ?= $(shell dpkg-parsechangelog -l$(PKD)/changelog | perl -ne 'print $$1 if m{^Version:\s+(?:\d+:)?(\d.*)(?:\-|\+nmu\d+.*)};') -DTYPE = -TARBALL = ../$(PKG)_$(VER)$(DTYPE).orig.tar.xz - -%: - dh $@ --with autoreconf - -override_dh_auto_configure: - CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" dh_auto_configure -- \ - --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) \ - --sysconfdir=/etc/zm --prefix=/usr --mandir=\$${prefix}/share/man \ - --infodir=\$${prefix}/share/info --with-mysql=/usr \ - --with-mariadb=/usr --with-webdir=/usr/share/zoneminder \ - --with-ffmpeg=/usr --with-cgidir=/usr/lib/cgi-bin \ - --with-webuser=www-data --with-webgroup=www-data \ - --enable-mmap=yes $(DEBOPT) - -override_dh_clean: - # Add here commands to clean up after the build process. - [ ! -f Makefile ] || $(MAKE) distclean - dh_clean src/zm_config_defines.h - # - # Delete remaining auto-generated Makefile if Makefile.in exists - find $(CURDIR)/ -type f -name "Makefile" | while read file; do \ - [ -f $$file.in ] && rm -f $$file; \ - done || true - # - # Delete remaining auto-generated Makefile.in if Makefile.am exists - find $(CURDIR)/ -type f -name "Makefile.in" | while read filein; do \ - fileam=`echo $$filein | sed 's/\(.*\)\.in/\1\.am/'`; \ - [ -f $$fileam ] && rm -f $$filein; \ - done || true - -override_dh_install: - dh_install --fail-missing - # - # NOTE: This is a short-term kludge; hopefully changes in the next - # upstream version will render this unnecessary. - rm -rf debian/zoneminder/usr/share/zoneminder/events - rm -rf debian/zoneminder/usr/share/zoneminder/images - rm -rf debian/zoneminder/usr/share/zoneminder/temp - # The link stuff for these folders has been moved to - # zoneminder-core.links file - # - # This is a slightly lesser kludge; moving the cgi stuff to - # /usr/share/zoneminder/cgi-bin breaks one set of behavior, - # having it just in /usr/lib/cgi-bin breaks another bit of - # behavior. - # The link stuff for /usr/share/zoneminder/cgi-bin has been moved to - # zoneminder-ui-base.links file - -override_dh_installinit: - dh_installinit --package=zoneminder-core --name=zoneminder - -override_dh_systemd_start: - dh_systemd_start --package=zoneminder-core --name=zoneminder \ - --restart-after-upgrade - -override_dh_systemd_enable: - dh_systemd_enable --package=zoneminder-core --name=zoneminder - -override_dh_fixperms: - dh_fixperms - # - # As requested by the Debian Webapps Policy Manual §3.2.1 - chown root:www-data debian/zoneminder-core/etc/zm/zm.conf - chmod 640 debian/zoneminder-core/etc/zm/zm.conf - -override_dh_auto_test: - # do not run tests... - -.PHONY: override_dh_strip -override_dh_strip: - dh_strip --dbg-package=zoneminder-core-dbg - -# Inspired by https://wiki.debian.org/onlyjob/get-orig-source -.PHONY: get-orig-source -get-orig-source: $(TARBALL) $(info I: $(PKG)_$(VER)$(DTYPE)) - @ - -$(TARBALL): - $(if $(wildcard $(PKG)-$(VER)),$(error folder '$(PKG)-$(VER)' exists, aborting...)) - @echo "# Cloning origin repository..."; \ - if ! git clone $(UURL) $(PKG)-$(VER); then \ - $(RM) -r $(PKG)-$(VER); \ - echo "failed to clone repository, aborting..."; \ - false; \ - fi - @if [ $(BRANCH) != "master" ]; then \ - cd $(PKG)-$(VER); \ - echo "# Not on master branch, fetching origin branch '$(BRANCH)'..."; \ - git fetch origin $(BRANCH):$(BRANCH) || false; \ - echo "# Switching to branch '$(BRANCH)'..."; \ - git checkout $(BRANCH) || false; \ - fi - @echo "# Checking local source..." - @if [ $$(cd $(PKG)-$(VER) && git rev-parse HEAD) = $(HEAD) ]; then \ - echo "even with origin, ok"; \ - true; \ - else \ - echo "not even with origin, aborting..."; \ - false; \ - fi - @echo "# Setting times..." - @cd $(PKG)-$(VER) \ - && for F in $$(git ls-tree -r --name-only HEAD | sed -e "s/\s/\*/g"); do \ - touch --no-dereference -d "$$(git log -1 --format="%ai" -- $$F)" "$$F"; \ - done - @echo "# Cleaning-up..." - cd $(PKG)-$(VER) && $(RM) -r .git - @echo "# Packing file '$(TARBALL)'..." - @find -L "$(PKG)-$(VER)" -xdev -type f -print | sort \ - | XZ_OPT="-6v" tar -caf "$(TARBALL)" -T- --owner=root --group=root --mode=a+rX \ - && $(RM) -r "$(PKG)-$(VER)" diff --git a/distros/ubuntu1410/source/format b/distros/ubuntu1410/source/format deleted file mode 100644 index 89ae9db8f..000000000 --- a/distros/ubuntu1410/source/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (native) diff --git a/distros/ubuntu1410/source/local-options b/distros/ubuntu1410/source/local-options deleted file mode 100644 index e69de29bb..000000000 diff --git a/distros/ubuntu1410/source/options b/distros/ubuntu1410/source/options deleted file mode 100644 index 8bd61fce6..000000000 --- a/distros/ubuntu1410/source/options +++ /dev/null @@ -1 +0,0 @@ -extend-diff-ignore = "(^|/)(config\.sub|config\.guess|Makefile|aclocal.m4|compile|config.h.in|configure|depcomp|install-sh|missing)$" diff --git a/distros/ubuntu1410/zoneminder-core.config b/distros/ubuntu1410/zoneminder-core.config deleted file mode 100644 index 2a15a599e..000000000 --- a/distros/ubuntu1410/zoneminder-core.config +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -# config maintainer script for zoneminder-core package - -set -e - -# Source the debconf stuff -. /usr/share/debconf/confmodule - -#DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-core.install b/distros/ubuntu1410/zoneminder-core.install deleted file mode 100644 index afd9ada95..000000000 --- a/distros/ubuntu1410/zoneminder-core.install +++ /dev/null @@ -1,4 +0,0 @@ -etc/zm -usr/bin -usr/share/polkit-1/actions -usr/share/polkit-1/rules.d diff --git a/distros/ubuntu1410/zoneminder-core.links b/distros/ubuntu1410/zoneminder-core.links deleted file mode 100644 index 5560a100a..000000000 --- a/distros/ubuntu1410/zoneminder-core.links +++ /dev/null @@ -1,3 +0,0 @@ -var/cache/zoneminder/events usr/share/zoneminder/events -var/cache/zoneminder/images usr/share/zoneminder/images -var/cache/zoneminder/temp usr/share/zoneminder/temp diff --git a/distros/ubuntu1410/zoneminder-core.postinst b/distros/ubuntu1410/zoneminder-core.postinst deleted file mode 100644 index da2b444fe..000000000 --- a/distros/ubuntu1410/zoneminder-core.postinst +++ /dev/null @@ -1,80 +0,0 @@ -#! /bin/sh -# postinst maintainer script for zoneminder-core package - -set -e - -# Source the debconf stuff -. /usr/share/debconf/confmodule - -# Source the config file -CONFIGFILE=/etc/zm/zm.conf -. $CONFIGFILE - -# Do this when the package is installed, upgraded or reconfigured -if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then - - # Retrieve data from dbconfig (inputs from user) - . /etc/dbconfig-common/zoneminder.conf - - # ZoneMinder config file handling - # Inspired by: http://manpages.debian.org/cgi-bin/man.cgi?query=debconf-devel&sektion=7 - - # Backup the config file - cp -a -f $CONFIGFILE ${CONFIGFILE}.postinst.bak - - # Redeclare variables if missing in config file - test -z $dbc_dbserver || grep -Eq "^ *ZM_DB_HOST=" $CONFIGFILE \ - || echo "ZM_DB_HOST=" >> ${CONFIGFILE}.postinst.bak - test -z $dbc_dbname || grep -Eq "^ *ZM_DB_NAME=" $CONFIGFILE \ - || echo "ZM_DB_NAME=" >> ${CONFIGFILE}.postinst.bak - test -z $dbc_dbuser || grep -Eq "^ *ZM_DB_USER=" $CONFIGFILE \ - || echo "ZM_DB_USER=" >> ${CONFIGFILE}.postinst.bak - test -z $dbc_dbpass || grep -Eq "^ *ZM_DB_PASS=" $CONFIGFILE \ - || echo "ZM_DB_PASS=" >> ${CONFIGFILE}.postinst.bak - - # Prevent ZM_DB_HOST to be empty if user selected the 'unix socket' method - if test -z $dbc_dbserver; then - dbc_dbserver_override="localhost" - else - dbc_dbserver_override=$dbc_dbserver - fi - - # Update variables in config file - sed -i "s/^ *ZM_DB_HOST=.*/ZM_DB_HOST=$dbc_dbserver_override/" \ - ${CONFIGFILE}.postinst.bak - test -z $dbc_dbname || sed -i "s/^ *ZM_DB_NAME=.*/ZM_DB_NAME=$dbc_dbname/" \ - ${CONFIGFILE}.postinst.bak - test -z $dbc_dbuser || sed -i "s/^ *ZM_DB_USER=.*/ZM_DB_USER=$dbc_dbuser/" \ - ${CONFIGFILE}.postinst.bak - test -z $dbc_dbpass || sed -i "s/^ *ZM_DB_PASS=.*/ZM_DB_PASS=$dbc_dbpass/" \ - ${CONFIGFILE}.postinst.bak - - # Clean-up backup file - mv -f ${CONFIGFILE}.postinst.bak $CONFIGFILE - - - # Set some file permissions - chown $ZM_WEB_USER:$ZM_WEB_GROUP /var/log/zm - if [ -z "$2" ]; then - chown $ZM_WEB_USER:$ZM_WEB_GROUP -R /var/cache/zoneminder - fi - # As requested by the Debian Webapps Policy Manual §3.2.1 - chown root:${ZM_WEB_GROUP} $CONFIGFILE - chmod 640 $CONFIGFILE -fi - -# Do this every time the package is installed or upgraded -# Test for database presence to avoid failure of zmupdate.pl -if [ "$dbc_install" = "true" ] && [ "$1" = "configure" ]; then - - # Ensure zoneminder is stopped - deb-systemd-invoke stop zoneminder.service || exit $? - - # Run the ZoneMinder update tool - zmupdate.pl --nointeractive - -fi - -#DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-core.postrm b/distros/ubuntu1410/zoneminder-core.postrm deleted file mode 100644 index d75e75e8b..000000000 --- a/distros/ubuntu1410/zoneminder-core.postrm +++ /dev/null @@ -1,37 +0,0 @@ -#! /bin/sh -# postrm maintainer script for zoneminder-core package - -set -e - -# Source the debconf stuff -if [ -f /usr/share/debconf/confmodule ]; then - . /usr/share/debconf/confmodule -fi - -if [ "$1" = "purge" ]; then - - # Ask the user if we have to remove the cache directory even if not empty - if [ -d /var/cache/zoneminder ] \ - && [ ! $(find /var/cache/zoneminder -maxdepth 0 -type d -empty 2>/dev/null) ]; then - RET="" - db_input high zoneminder/ask_delete || true - db_go || true - db_get zoneminder/ask_delete - if [ "$RET" = "true" ]; then - RET="" - db_input high zoneminder/ask_delete_again || true - db_go || true - db_get zoneminder/ask_delete_again - if [ "$RET" = "true" ]; then - rm -rf /var/cache/zoneminder - fi - fi - fi -fi - -#DEBHELPER# - -# postrm rm may freeze without that -db_stop - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-core.preinst b/distros/ubuntu1410/zoneminder-core.preinst deleted file mode 100644 index 3ed1ef661..000000000 --- a/distros/ubuntu1410/zoneminder-core.preinst +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh -# preinst maintainer script for zoneminder-core package - -set -e - -abort=false -if [ -L /usr/share/zoneminder/events ]; then - l=$(readlink /usr/share/zoneminder/events) - if [ "$l" != "/var/cache/zoneminder/events" ]; then - abort=true - fi -fi -if [ -L /usr/share/zoneminder/images ]; then - l=$(readlink /usr/share/zoneminder/images ) - if [ "$l" != "/var/cache/zoneminder/images" ]; then - abort=true - fi -fi - -if [ "$abort" = "true" ]; then - cat >&2 << EOF -Aborting installation of zoneminder due to non-default symlinks in -/usr/share/zoneminder for the images and/or events directory, which could -result in loss of data. Please move your data in each of these directories to -/var/cache/zoneminder before installing zoneminder from the package. -EOF - exit 1 - -fi - -#DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-core.templates b/distros/ubuntu1410/zoneminder-core.templates deleted file mode 100644 index 35fdefd7a..000000000 --- a/distros/ubuntu1410/zoneminder-core.templates +++ /dev/null @@ -1,19 +0,0 @@ -Template: zoneminder/ask_delete -Type: boolean -Default: false -_Description: Delete this non empty directory? - A purge of the ZoneMinder package is performed but the directory - '/var/cache/zoneminder' is not empty so it will not be deleted. - . - Please consider that this directory is designed to contain data resulting from - event detection. Therefore, "proof of evidence" could be lost!" - . - If you are not sure of your decision, please do not delete this directory but - perform a manual checkup. - -Template: zoneminder/ask_delete_again -Type: boolean -Default: false -_Description: Deletion confirmed? - You have allowed the deletion of directory '/var/cache/zoneminder' although - it may contain critical data. diff --git a/distros/ubuntu1410/zoneminder-core.zoneminder.service b/distros/ubuntu1410/zoneminder-core.zoneminder.service deleted file mode 100644 index d82270024..000000000 --- a/distros/ubuntu1410/zoneminder-core.zoneminder.service +++ /dev/null @@ -1,19 +0,0 @@ -# ZoneMinder systemd unit file -# This file is intended to work with debian distributions - -[Unit] -Description=ZoneMinder CCTV recording and security system -After=network.target mysql.service apache2.service -Requires=apache2.service -Wants=mysql.service - -[Service] -User=www-data -Type=forking -ExecStart=/usr/bin/zmpkg.pl start -ExecReload=/usr/bin/zmpkg.pl restart -ExecStop=/usr/bin/zmpkg.pl stop -PIDFile=/var/run/zm/zm.pid - -[Install] -WantedBy=multi-user.target diff --git a/distros/ubuntu1410/zoneminder-core.zoneminder.tmpfile b/distros/ubuntu1410/zoneminder-core.zoneminder.tmpfile deleted file mode 100644 index 6ea70bf35..000000000 --- a/distros/ubuntu1410/zoneminder-core.zoneminder.tmpfile +++ /dev/null @@ -1 +0,0 @@ -d /var/run/zm 0755 www-data www-data diff --git a/distros/ubuntu1410/zoneminder-database.config b/distros/ubuntu1410/zoneminder-database.config deleted file mode 100644 index f6a84d36d..000000000 --- a/distros/ubuntu1410/zoneminder-database.config +++ /dev/null @@ -1,142 +0,0 @@ -#!/bin/sh -# config maintainer script for zoneminder - -set -e - -# Source the debconf stuff -. /usr/share/debconf/confmodule - -# Set the first version in which dbconfig-common was introduced in the package -dbc_first_version="1.28.0" - -CONFIGFILE=/etc/zm/zm.conf -if [ -e $CONFIGFILE ]; then - # Source the config file if exists - . $CONFIGFILE -elif [ -e ${CONFIGFILE}.dpkg-new ]; then - # If no config file, source the config file which is going to be installed - # by the core package - . ${CONFIGFILE}.dpkg-new -else - # If no config file is going to be installed, set some default values - ZM_DB_HOST= - ZM_DB_NAME="zm" - ZM_DB_USER="zmuser" -fi - -# Set some variables for the dbconfig-common stuff -dbc_dbserver="$ZM_DB_HOST" -dbc_dbname="$ZM_DB_NAME" -dbc_dbuser="$ZM_DB_USER" - -if [ -f /usr/share/dbconfig-common/dpkg/config ]; then - - # Default use dbconfig-common - dbc_install="true" - - # Currently we only support mysql database - dbc_dbtypes="mysql" - - # Set authentication method to password - dbc_authmethod_user="password" - - # Source the dbconfig-common stuff - . /usr/share/dbconfig-common/dpkg/config -fi - -# Do this when the package is installed, upgraded or reconfigured -# Most of answers are cached so the questions will not be asked again -if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then - - # Ask the user if the database shall be installed locally or remotely - db_input high zoneminder/debconf_dblocation || true - db_go || true - db_get zoneminder/debconf_dblocation - - if [ "$RET" = "local" ]; then - if [ ! -e "/usr/sbin/mysqld" ]; then - # Display a message and exit if the user want a local database but - # no database server is available - db_input high zoneminder/debconf_dblocalmissingerror || true - db_go || true - exit 0 - else - # Set the database server to localhost - dbc_dbserver="localhost" - fi - else - # Source the dbconfig main configuration file - if [ -f /etc/dbconfig-common/config ]; then - . /etc/dbconfig-common/config - fi - if [ "$dbc_remote_questions_default" = "false" ]; then - # Display a message and exit if the dbconfig configuration does not - # allow installation of remote databases - # Note: To overcome this issue, we could think to override the - # default setting by using dbc_remote_questions_default='true' in - # maintainer scripts but unfortunately this does not work due to - # current dbconfig design - # More information here: - # https://bugs.launchpad.net/ubuntu/+source/dbconfig-common/+bug/1065331 - db_input high zoneminder/debconf_dbconfigerror || true - db_go || true - exit 0 - fi - fi - - # Ask the user for all database settings - dbc_go zoneminder $@ - - # Ask the user for the password of the database administrator if the user - # has not yet answered to this question. - # This situation may occur if the user skipped the database creation step - # when reconfiguring the package. - RET="" - db_get zoneminder/mysql/admin-pass - if [ -z "$RET" ]; then - db_input high zoneminder/mysql/admin-pass || true - db_go || true - fi - - # Do this only when not upgrading the package (no old version in argument) - if [ -z "$2" ]; then - # Ask for the password of 'admin' user - while :; do - RET="" - db_input high zoneminder/admin_password || true - db_go || true - db_get zoneminder/admin_password - # If password isn't empty we ask for password verification - if [ -z "$RET" ]; then - db_fset zoneminder/admin_password seen false - db_fset zoneminder/admin_password_again seen false - break - fi - ROOT_PW="$RET" - db_input high zoneminder/admin_password_again || true - db_go || true - db_get zoneminder/admin_password_again - if [ "$RET" = "$ROOT_PW" ]; then - ROOT_PW="" - break - fi - db_fset zoneminder/password_mismatch seen false - db_input critical zoneminder/password_mismatch || true - db_set zoneminder/admin_password "" - db_set zoneminder/admin_password_again "" - db_go || true - done - else - # If we are upgrading the package, set an empty password to disable - # password update in ZoneMinder database - db_set zoneminder/admin_password "" - fi - # Set the seen flag to not ask this question again if no password is - # provided - db_fset zoneminder/admin_password seen true - -fi - -#DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-database.dirs b/distros/ubuntu1410/zoneminder-database.dirs deleted file mode 100644 index b37463a9e..000000000 --- a/distros/ubuntu1410/zoneminder-database.dirs +++ /dev/null @@ -1,3 +0,0 @@ -usr/share/zoneminder/db -usr/share/dbconfig-common/data/zoneminder/install -usr/share/dbconfig-common/data/zoneminder/upgrade/mysql diff --git a/distros/ubuntu1410/zoneminder-database.install b/distros/ubuntu1410/zoneminder-database.install deleted file mode 100644 index 756c5bbfa..000000000 --- a/distros/ubuntu1410/zoneminder-database.install +++ /dev/null @@ -1 +0,0 @@ -usr/share/zoneminder/db diff --git a/distros/ubuntu1410/zoneminder-database.postinst b/distros/ubuntu1410/zoneminder-database.postinst deleted file mode 100644 index 41d4e5b5b..000000000 --- a/distros/ubuntu1410/zoneminder-database.postinst +++ /dev/null @@ -1,79 +0,0 @@ -#! /bin/sh -# postinst maintainer script for zoneminder-db package - -set -e - -# Source the debconf stuff -. /usr/share/debconf/confmodule - -mysql_update() { - - # Source the dbconfig stuff - . /usr/share/dbconfig-common/internal/mysql - - # Update the password of the hard-coded default 'admin' account - test -z $ADMIN_PASSWORD || dbc_mysql_exec_command "UPDATE Users SET Password = password('$ADMIN_PASSWORD') WHERE Username = 'admin';" || true - - # Update the database version - dbc_mysql_exec_command "UPDATE Config SET Value = '$DB_VERSION' WHERE Name = 'ZM_DYN_DB_VERSION';" || true -} - -if [ -f /usr/share/dbconfig-common/dpkg/postinst ]; then - - # Set the first version in which dbconfig-common was introduced in the package - dbc_first_version="1.28.0" - - # Set the database type - dbc_dbtypes="mysql" - - # Source the dbconfig-common stuff - . /usr/share/dbconfig-common/dpkg/postinst -fi - -# Do this when the package is installed, upgraded or reconfigured -if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then - - # Install sql database create file for dbconfig - # (needed at first package installation) - if [ ! -f /usr/share/dbconfig-common/data/zoneminder/install/mysql ]; then - install -m 644 /usr/share/zoneminder/db/zm_create.sql \ - /usr/share/dbconfig-common/data/zoneminder/install/mysql - # Remove unneeded sql requests - # dbconfig will create the underlying database - sed -i "/^ *CREATE DATABASE /d" \ - /usr/share/dbconfig-common/data/zoneminder/install/mysql - sed -i "/^ *USE /d" \ - /usr/share/dbconfig-common/data/zoneminder/install/mysql - fi - - # Symlink sql update files for dbconfig (needed when upgrading the package) - for sqlfile in /usr/share/zoneminder/db/zm_update-*.sql; do - lnk=`echo $sqlfile | sed "s/^\/usr\/share\/zoneminder\/db\/zm_update-\(.*\)\.sql/\1/"` - if [ ! -L /usr/share/dbconfig-common/data/zoneminder/upgrade/mysql/$lnk ]; then - ln -sf $sqlfile \ - /usr/share/dbconfig-common/data/zoneminder/upgrade/mysql/$lnk - fi - done || true - - # Create the underlying database and populate it - # dbconfig will take care of applying any updates which are newer than the - # previously installed version - dbc_go zoneminder $@ - - # Get the password of ZoneMinder user 'admin' from debconf - db_get zoneminder/admin_password - ADMIN_PASSWORD=$RET - - # Remove the password from debconf database - test -z $ADMIN_PASSWORD || db_reset zoneminder/admin_password || true - - # Get the lastest database version from dbconfig upgrade folder - DB_VERSION=$(ls -rv /usr/share/dbconfig-common/data/zoneminder/upgrade/$dbc_dbtypes | head -1) - - # Update the default admin account and database version - mysql_update -fi - -#DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-database.postrm b/distros/ubuntu1410/zoneminder-database.postrm deleted file mode 100644 index 231f01ad7..000000000 --- a/distros/ubuntu1410/zoneminder-database.postrm +++ /dev/null @@ -1,34 +0,0 @@ -#! /bin/sh -# postrm maintainer script for zoneminder-db package - -set -e - -# Source the debconf stuff -if [ -f /usr/share/debconf/confmodule ]; then - . /usr/share/debconf/confmodule -fi - -# Source the dbconfig stuff -if [ -f /usr/share/dbconfig-common/dpkg/postrm ]; then - . /usr/share/dbconfig-common/dpkg/postrm - # Ask the user what do to with dbconfig when removing the package - dbc_go zoneminder $@ -fi - -if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then - # Remove dbconfig stuff added in postinst script - rm -rf /usr/share/dbconfig-common/data/zoneminder - # No need to manually remove the zm database, dbconfig take care of this -fi - -if [ "$1" = "purge" ]; then - # Delete a potential remaining file used in postinst script - rm -f /etc/zm/zm.conf.postinst.bak -fi - -#DEBHELPER# - -# postrm rm may freeze without that -db_stop - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-database.prerm b/distros/ubuntu1410/zoneminder-database.prerm deleted file mode 100644 index 31786116a..000000000 --- a/distros/ubuntu1410/zoneminder-database.prerm +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh -# prerm script for zoneminder-db package - -set -e - -# Source the debconf stuff if file exists -if [ -f /usr/share/debconf/confmodule ]; then - . /usr/share/debconf/confmodule -fi - -# If dbconfig-common is installed and has been used by zoneminder -if [ -f /usr/share/dbconfig-common/dpkg/prerm ] \ - && [ -f /etc/dbconfig-common/zoneminder.conf ]; then - # Source the dbconfig stuff - . /usr/share/dbconfig-common/dpkg/prerm - # Ask the user what do to with dbconfig before removing the package - dbc_go zoneminder $@ -fi - -# #DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-database.templates b/distros/ubuntu1410/zoneminder-database.templates deleted file mode 100644 index 4de4342f6..000000000 --- a/distros/ubuntu1410/zoneminder-database.templates +++ /dev/null @@ -1,58 +0,0 @@ -Template: zoneminder/debconf_dblocation -Type: select -__Choices: local, remote -Default: local -_Description: Database location: - A database server is required to run ZoneMinder. The database can be installed - either locally or remotely on a machine of your network. - . - If you choose a remote location, you will have to select the 'tcp/ip' - connection method and enter the hostname or ip address of the remote machine - in the next configuration screens. - -Template: zoneminder/debconf_dblocalmissingerror -Type: error -_Description: No local database server is available: - Currently ZoneMinder supports mysql or mariadb database server but none of them - appears to be installed on this machine. - . - In order to complete ZoneMinder's installation, after ending of this assistant, - please install a compatible database server and then restart the assistant by - invoking: - . - $ sudo dpkg-reconfigure zoneminder - -Template: zoneminder/debconf_dbconfigerror -Type: error -_Description: Remote database servers are not allowed: - The current configuration of dbconfig-common does not allow installation of - a database on remote servers. - . - In order to reconfigure dbconfig-common, please invoke the following command - after ending of this assistant: - . - $ sudo dpkg-reconfigure dbconfig-common - . - Then, to complete ZoneMinder's installation, please restart this assistant by - invoking: - . - $ sudo dpkg-reconfigure zoneminder - -Template: zoneminder/admin_password -Type: password -_Description: New password for the ZoneMinder 'admin' user: - Please enter the password of the default administrative user. - . - While not mandatory, it is highly recommended that you set a custom password - for the administrative 'admin' user. - . - If this field is left blank, the password will not be changed. - -Template: zoneminder/admin_password_again -Type: password -_Description: Repeat password for the ZoneMinder 'admin' user: - -Template: zoneminder/password_mismatch -Type: error -_Description: Password input error - The two passwords you entered were not the same. Please try again. diff --git a/distros/ubuntu1410/zoneminder-ui-base.config b/distros/ubuntu1410/zoneminder-ui-base.config deleted file mode 100644 index 2660208a8..000000000 --- a/distros/ubuntu1410/zoneminder-ui-base.config +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -# config maintainer script for zoneminder-ui-base package - -set -e - -# Source the debconf stuff -. /usr/share/debconf/confmodule - -# Do this when the package is installed, upgraded or reconfigured -# Most of answers are cached so the questions will not be asked again -if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then - - # Ask the user for the web server(s) to configure - db_input high zoneminder/webserver || true - db_go || true -fi - -#DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-ui-base.install b/distros/ubuntu1410/zoneminder-ui-base.install deleted file mode 100644 index f72b569be..000000000 --- a/distros/ubuntu1410/zoneminder-ui-base.install +++ /dev/null @@ -1,11 +0,0 @@ -debian/apache.conf etc/zm -usr/lib/cgi-bin -usr/share/zoneminder/ajax -usr/share/zoneminder/css -usr/share/zoneminder/graphics -usr/share/zoneminder/includes -usr/share/zoneminder/index.php -usr/share/zoneminder/js -usr/share/zoneminder/lang -usr/share/zoneminder/tools -usr/share/zoneminder/views diff --git a/distros/ubuntu1410/zoneminder-ui-base.links b/distros/ubuntu1410/zoneminder-ui-base.links deleted file mode 100644 index b00a147d6..000000000 --- a/distros/ubuntu1410/zoneminder-ui-base.links +++ /dev/null @@ -1 +0,0 @@ -usr/lib/cgi-bin usr/share/zoneminder/cgi-bin diff --git a/distros/ubuntu1410/zoneminder-ui-base.postinst b/distros/ubuntu1410/zoneminder-ui-base.postinst deleted file mode 100644 index a5bce3c98..000000000 --- a/distros/ubuntu1410/zoneminder-ui-base.postinst +++ /dev/null @@ -1,48 +0,0 @@ -#! /bin/sh -# postinst maintainer script for zoneminder-ui-base package - -set -e - -# Source the debconf stuff -. /usr/share/debconf/confmodule - -apache_install() { - - mkdir -p /etc/apache2/conf-available - ln -sf ../../zm/apache.conf /etc/apache2/conf-available/zoneminder.conf - - COMMON_STATE=$(dpkg-query -f '${Status}' -W 'apache2.2-common' 2>/dev/null | awk '{print $3}' || true) - - if [ -e /usr/share/apache2/apache2-maintscript-helper ] ; then - . /usr/share/apache2/apache2-maintscript-helper - apache2_invoke enconf zoneminder - elif [ "$COMMON_STATE" = "installed" ] || [ "$COMMON_STATE" = "unpacked" ] ; then - [ -d /etc/apache2/conf.d/ ] && [ ! -L /etc/apache2/conf.d/zoneminder.conf ] && ln -s ../conf-available/zoneminder.conf /etc/apache2/conf.d/zoneminder.conf - fi - - # Enable CGI script module in apache (not enabled by default on jessie) - a2enmod cgi >/dev/null 2>&1 - - # Reload the web server - deb-systemd-invoke reload apache2.service || true -} - -# Do this when the package is installed, upgraded or reconfigured -if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then - - # Configure the web server - db_get zoneminder/webserver - webservers="$RET" - - for webserver in $webservers; do - webserver=${webserver%,} - # Currently we only support apache2 - if [ "$webserver" = "apache2" ] ; then - apache_install $1 - fi - done -fi - -#DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-ui-base.postrm b/distros/ubuntu1410/zoneminder-ui-base.postrm deleted file mode 100644 index 441bb5218..000000000 --- a/distros/ubuntu1410/zoneminder-ui-base.postrm +++ /dev/null @@ -1,41 +0,0 @@ -#! /bin/sh -# postrm maintainer script for zoneminder-ui-base package - -set -e - -# Source the debconf stuff -if [ -f /usr/share/debconf/confmodule ]; then - . /usr/share/debconf/confmodule -fi - -apache_remove() { - COMMON_STATE=$(dpkg-query -f '${Status}' -W 'apache2.2-common' 2>/dev/null | awk '{print $3}' || true) - if [ -e /usr/share/apache2/apache2-maintscript-helper ] ; then - . /usr/share/apache2/apache2-maintscript-helper - apache2_invoke disconf zoneminder - elif [ "$COMMON_STATE" = "installed" ] || [ "$COMMON_STATE" = "unpacked" ] ; then - rm -f /etc/apache2/conf.d/zoneminder.conf - fi - rm -f /etc/apache2/conf-available/zoneminder.conf - # Reload the web server - deb-systemd-invoke reload apache2.service || true -} - -if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then - # Deconfigure the web server - db_get zoneminder/webserver - for webserver in $RET; do - webserver=${webserver%,} - # Currently we only support apache2 - if [ "$webserver" = "apache2" ] ; then - apache_remove $1 - fi - done -fi - -#DEBHELPER# - -# postrm rm may freeze without that -db_stop - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-ui-base.templates b/distros/ubuntu1410/zoneminder-ui-base.templates deleted file mode 100644 index 31e70277f..000000000 --- a/distros/ubuntu1410/zoneminder-ui-base.templates +++ /dev/null @@ -1,7 +0,0 @@ -Template: zoneminder/webserver -Type: multiselect -Choices: apache2 -Default: apache2 -_Description: Web server to reconfigure automatically: - Please choose the web server that should be automatically configured for - ZoneMinder's web portal access. diff --git a/distros/ubuntu1410/zoneminder-ui-classic.install b/distros/ubuntu1410/zoneminder-ui-classic.install deleted file mode 100644 index 9532d9dc9..000000000 --- a/distros/ubuntu1410/zoneminder-ui-classic.install +++ /dev/null @@ -1 +0,0 @@ -usr/share/zoneminder/skins/classic diff --git a/distros/ubuntu1410/zoneminder-ui-mobile.install b/distros/ubuntu1410/zoneminder-ui-mobile.install deleted file mode 100644 index 464bb74eb..000000000 --- a/distros/ubuntu1410/zoneminder-ui-mobile.install +++ /dev/null @@ -1 +0,0 @@ -usr/share/zoneminder/skins/mobile diff --git a/distros/ubuntu1410/zoneminder-ui-xml.install b/distros/ubuntu1410/zoneminder-ui-xml.install deleted file mode 100644 index 6617707f8..000000000 --- a/distros/ubuntu1410/zoneminder-ui-xml.install +++ /dev/null @@ -1 +0,0 @@ -usr/share/zoneminder/skins/xml diff --git a/distros/ubuntu1604/changelog b/distros/ubuntu1604/changelog index 616f75178..0fc2fda2e 100644 --- a/distros/ubuntu1604/changelog +++ b/distros/ubuntu1604/changelog @@ -1,3 +1,3 @@ -zoneminder (1.31.39~20180223.27-stretch-1) unstable; urgency=low - * - -- Isaac Connor Fri, 23 Feb 2018 14:15:59 -0500 +zoneminder (1.35.6~20200825.27-xenial) xenial; urgency=low + * + -- Isaac Connor Tue, 25 Aug 2020 09:28:18 -0400 diff --git a/distros/ubuntu1604/conf/apache2/zoneminder.conf b/distros/ubuntu1604/conf/apache2/zoneminder.conf index 598996bc0..e3164d36c 100644 --- a/distros/ubuntu1604/conf/apache2/zoneminder.conf +++ b/distros/ubuntu1604/conf/apache2/zoneminder.conf @@ -6,6 +6,7 @@ ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin" Require all granted + # Order matters. This alias must come first. Alias /zm/cache /var/cache/zoneminder/cache diff --git a/distros/ubuntu1604/control b/distros/ubuntu1604/control index 68bc1757f..34af7cef1 100644 --- a/distros/ubuntu1604/control +++ b/distros/ubuntu1604/control @@ -1,8 +1,8 @@ Source: zoneminder Section: net Priority: optional -Maintainer: Dmitry Smirnov -Uploaders: Vagrant Cascadian +Maintainer: Isaac Connor +Uploaders: Isaac Connor Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apache2-dev, dh-linktree, dh-systemd, dh-apache2 ,cmake ,libx264-dev, libmp4v2-dev @@ -10,7 +10,7 @@ Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apa ,libavcodec-dev (>= 6:10~) ,libavformat-dev (>= 6:10~) ,libavutil-dev (>= 6:10~) - ,libswresample-dev + ,libswresample-dev | libavresample-dev ,libswscale-dev (>= 6:10~) ,ffmpeg | libav-tools ,net-tools @@ -33,6 +33,7 @@ Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apa ,libssl-dev ,libcrypt-eksblowfish-perl ,libdata-entropy-perl + ,libvncserver-dev # Unbundled (dh_linktree): ,libjs-jquery ,libjs-mootools @@ -61,8 +62,9 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,libjson-maybexs-perl ,libsys-mmap-perl [!hurd-any] ,liburi-encode-perl - ,libwww-perl + ,libwww-perl, liburi-perl ,libdata-dump-perl + ,libdatetime-perl ,libclass-std-fast-perl ,libsoap-wsdl-perl ,libio-socket-multicast-perl @@ -73,7 +75,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 | php5-json ,policykit-1 ,rsyslog | system-log-daemon ,zip @@ -81,6 +83,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,libssl | libssl1.0.0 | libssl1.1 ,libcrypt-eksblowfish-perl ,libdata-entropy-perl + ,libvncclient1|libvncclient0 Recommends: ${misc:Recommends} ,libapache2-mod-php5 | libapache2-mod-php | php5-fpm | php-fpm ,mysql-server | mariadb-server | virtual-mysql-server diff --git a/distros/ubuntu1604/rules b/distros/ubuntu1604/rules index c671a1b03..9a16b1f8f 100755 --- a/distros/ubuntu1604/rules +++ b/distros/ubuntu1604/rules @@ -21,8 +21,8 @@ override_dh_auto_configure: -DCMAKE_BUILD_TYPE=Release \ -DZM_CONFIG_DIR="/etc/zm" \ -DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \ - -DZM_RUNDIR="/var/run/zm" \ - -DZM_SOCKDIR="/var/run/zm" \ + -DZM_RUNDIR="/run/zm" \ + -DZM_SOCKDIR="/run/zm" \ -DZM_TMPDIR="/tmp/zm" \ -DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \ -DZM_CACHEDIR="/var/cache/zoneminder/cache" \ diff --git a/distros/ubuntu1604/zoneminder.logrotate b/distros/ubuntu1604/zoneminder.logrotate index 59238b7fe..6162e9c4d 100644 --- a/distros/ubuntu1604/zoneminder.logrotate +++ b/distros/ubuntu1604/zoneminder.logrotate @@ -9,4 +9,5 @@ endscript daily rotate 7 + maxage 7 } diff --git a/distros/ubuntu1604/zoneminder.postinst b/distros/ubuntu1604/zoneminder.postinst index 9ee8b032c..e9bd30a17 100644 --- a/distros/ubuntu1604/zoneminder.postinst +++ b/distros/ubuntu1604/zoneminder.postinst @@ -19,6 +19,10 @@ if [ "$1" = "configure" ]; then echo "The cgi module is not enabled in apache2. I am enabling it using a2enmod cgi." a2enmod cgi fi + if [ ! -e "/etc/apache2/mods-enabled/rewrite.load" ] && [ "$(command -v a2enmod)" != "" ]; then + echo "The rewrite module is not enabled in apache2. I am enabling it using a2enmod rewrite." + a2enmod rewrite + fi if [ "$ZM_DB_HOST" = "localhost" ]; then @@ -61,11 +65,10 @@ if [ "$1" = "configure" ]; then exit 1; fi # This creates the user. - 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}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql - else - 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}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + echo "CREATE USER '${ZM_DB_USER}'@localhost 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}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql zmupdate.pl --nointeractive zmupdate.pl --nointeractive -f diff --git a/distros/ubuntu2004/NEWS b/distros/ubuntu2004/NEWS new file mode 100644 index 000000000..6200726cf --- /dev/null +++ b/distros/ubuntu2004/NEWS @@ -0,0 +1,10 @@ +zoneminder (1.28.1-1) unstable; urgency=low + + This version is no longer automatically initialize or upgrade database. + See README.Debian for details. + + Changed installation paths (please correct your web server configuration): + /usr/share/zoneminder --> /usr/share/zoneminder/www + /usr/lib/cgi-bin --> /usr/lib/zoneminder/cgi-bin + + -- Dmitry Smirnov Tue, 31 Mar 2015 15:12:17 +1100 diff --git a/distros/ubuntu2004/README.Debian b/distros/ubuntu2004/README.Debian new file mode 100644 index 000000000..4fe3464d2 --- /dev/null +++ b/distros/ubuntu2004/README.Debian @@ -0,0 +1,130 @@ +Zoneminder for Debian +--------------------- + +Initializing database +--------------------- + + pv /usr/share/zoneminder/db/zm_create.sql | sudo mysql --defaults-file=/etc/mysql/debian.cnf +OR + cat /usr/share/zoneminder/db/zm_create.sql | sudo mysql --defaults-file=/etc/mysql/debian.cnf + + echo 'grant lock tables,alter,create,index,select,insert,update,delete on zm.* to 'zmuser'@localhost identified by "zmpass";'\ + | sudo mysql --defaults-file=/etc/mysql/debian.cnf mysql + +Hint: generate secure password with `pwgen` and update "/etc/zm/zm.conf" +accordingly. + +The following command can help to ensure that zoneminder can read its +configuration file: + + chgrp -c www-data /etc/zm/zm.conf + + +Upgrading database +------------------ + +The database is updated automatically on installation. You should not need to take this step. + +Assuming that database is on "localhost" then the following command can be +used to upgrade "zm" database: + + zmupdate.pl + +Additional permissions may be required to perform upgrade: + + echo 'grant lock tables, create, alter on zm.* to 'zmuser'@localhost identified by "zmpass";'\ + | sudo mysql --defaults-file=/etc/mysql/debian.cnf mysql + +The following command prints the current version of zoneminder database: + + echo 'select Value from Config where Name = "ZM_DYN_CURR_VERSION";' \ + | sudo mysql --defaults-file=/etc/mysql/debian.cnf --skip-column-names zm + + +Enabling service +---------------- + +By default Zoneminder service is not automatically started and needs to be +manually enabled once database is configured: + + sudo systemctl enable zoneminder.service + + +Web server set-up +----------------- + +There are few manual steps to get the web interface working: + +## Apache2 + +Apache can be configured as folder "/zm" using sample .conf: + + sudo a2enconf zoneminder + +Alternatively Apache web site configuration template can be used to setup +zoneminder as "http://zoneminder": + + sudo cp -v /usr/share/doc/zoneminder/examples/apache.conf /etc/apache2/sites-available/ + sudo a2ensite zoneminder.conf + +Common configuration steps for Apache2: + + sudo a2enmod cgi + sudo service apache2 reload + + +## nginx / fcgiwrap + +Nginx needs "php-fpm" package to support PHP and "fcgiwrap" package +for binary "cgi-bin" applications: + + sudo apt-get install php-fpm fcgiwrap + +To enable a URL alias that makes Zoneminder available from + + http://yourserver/zm + +the following line is to be added to "server" section of a web site +configuration: + + include /usr/share/doc/zoneminder/examples/nginx.conf; + +For "default" web site it would be sufficient to include the above +statement to the file + + /etc/nginx/sites-enabled/default + +To avoid problems with feeds from multiple cameras "fcgiwrap" should be +configured to start at least as many processes as there are cameras. +It can be done by adjusting DAEMON_OPTS in "/etc/default/fcgiwrap". +Systemd users may be affected by the following bug: + + http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=792705 + + +## Note: + +When Zoneminder web site is running it may be necessary to set +Options/Paths/PATH_ZMS to "/zm/cgi-bin/nph-zms" or according to chosen web +site configuration. + + +Changing the location for images and events +------------------------------------------- + +ZoneMinder is now able to be configured to use an alternative location for storing +events and images at compile time. This package makes use of that, so symlinks in +/usr/share/zoneminder/www are no longer necessary. + +Access to /dev/video* +--------------------- + +For cameras which require access to /dev/video*, zoneminder may need the +www-data user added to the video group in order to see those cameras: + + adduser www-data video + +Note that all web applications running on the zoneminder server will then have +access to all video devices on the system. + + -- Vagrant Cascadian Sun, 27 Mar 2011 13:06:56 -0700 diff --git a/distros/ubuntu2004/TODO.Debian b/distros/ubuntu2004/TODO.Debian new file mode 100644 index 000000000..9dc59613b --- /dev/null +++ b/distros/ubuntu2004/TODO.Debian @@ -0,0 +1,12 @@ + +## Separate substantial /usr/share into its own arch-all package. + +## Decide how to handle database updates. + + * Consider possibility that database may be on another machine (#469239). + * Consider dbconfig-common? Probably not (what if database is not on localhost?). + +### Run `zmupdate.pl` from service control scripts (init.d, service) on start? + + Automatic upgrade will break "one DB, many zoneminders" setup (unimportant?). + diff --git a/distros/ubuntu2004/changelog b/distros/ubuntu2004/changelog new file mode 100644 index 000000000..616f75178 --- /dev/null +++ b/distros/ubuntu2004/changelog @@ -0,0 +1,3 @@ +zoneminder (1.31.39~20180223.27-stretch-1) unstable; urgency=low + * + -- Isaac Connor Fri, 23 Feb 2018 14:15:59 -0500 diff --git a/distros/ubuntu2004/clean b/distros/ubuntu2004/clean new file mode 100644 index 000000000..941ef2a3a --- /dev/null +++ b/distros/ubuntu2004/clean @@ -0,0 +1,3 @@ +.gitattributes +web/api/.gitattributes +web/api/.gitignore diff --git a/distros/ubuntu1410/compat b/distros/ubuntu2004/compat similarity index 100% rename from distros/ubuntu1410/compat rename to distros/ubuntu2004/compat diff --git a/distros/ubuntu2004/conf/apache2/zoneminder.conf b/distros/ubuntu2004/conf/apache2/zoneminder.conf new file mode 100644 index 000000000..e3164d36c --- /dev/null +++ b/distros/ubuntu2004/conf/apache2/zoneminder.conf @@ -0,0 +1,57 @@ +# Remember to enable cgi mod (i.e. "a2enmod cgi"). +ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin" + + Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch + AllowOverride All + Require all granted + + + +# Order matters. This alias must come first. +Alias /zm/cache /var/cache/zoneminder/cache + + Options -Indexes +FollowSymLinks + AllowOverride None + + # Apache 2.4 + Require all granted + + + # Apache 2.2 + Order deny,allow + Allow from all + + + +Alias /zm /usr/share/zoneminder/www + + Options -Indexes +FollowSymLinks + + DirectoryIndex index.php + + + +# For better visibility, the following directives have been migrated from the +# default .htaccess files included with the CakePHP project. +# Parameters not set here are inherited from the parent directive above. + + RewriteEngine on + RewriteRule ^$ app/webroot/ [L] + RewriteRule (.*) app/webroot/$1 [L] + RewriteBase /zm/api + + + + RewriteEngine on + RewriteRule ^$ webroot/ [L] + RewriteRule (.*) webroot/$1 [L] + RewriteBase /zm/api + + + + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [L] + RewriteBase /zm/api + diff --git a/distros/ubuntu2004/control b/distros/ubuntu2004/control new file mode 100644 index 000000000..0a1f9e0de --- /dev/null +++ b/distros/ubuntu2004/control @@ -0,0 +1,166 @@ +Source: zoneminder +Section: net +Priority: optional +Maintainer: Isaac Connor +Uploaders: Isaac Connor +Build-Depends: debhelper, dh-systemd, sphinx-doc, python3-sphinx, dh-linktree, dh-systemd, dh-apache2 + ,cmake + ,libx264-dev, libmp4v2-dev + ,libavdevice-dev + ,libavcodec-dev + ,libavformat-dev + ,libavutil-dev + ,libswresample-dev + ,libswscale-dev + ,ffmpeg + ,net-tools + ,libbz2-dev + ,libgcrypt20-dev + ,libcurl4-gnutls-dev + ,libturbojpeg0-dev + ,default-libmysqlclient-dev | libmysqlclient-dev | libmariadbclient-dev-compat + ,libpcre3-dev + ,libpolkit-gobject-1-dev + ,libv4l-dev [!hurd-any] + ,libvlc-dev + ,libdate-manip-perl + ,libdbd-mysql-perl + ,libphp-serialization-perl + ,libsys-mmap-perl [!hurd-any] + ,libwww-perl + ,libdata-uuid-perl + ,libssl-dev + ,libcrypt-eksblowfish-perl + ,libdata-entropy-perl +# Unbundled (dh_linktree): + ,libjs-jquery + ,libjs-mootools +Standards-Version: 3.9.8 +Homepage: http://www.zoneminder.com/ +Vcs-Browser: http://anonscm.debian.org/cgit/collab-maint/zoneminder.git +Vcs-Git: git://anonscm.debian.org/collab-maint/zoneminder.git + +Package: zoneminder +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} + ,javascript-common + ,libmp4v2-2, libx264-155 + ,libswscale5 + ,libswresample3 + ,ffmpeg + ,libdate-manip-perl, libmime-lite-perl, libmime-tools-perl + ,libdbd-mysql-perl + ,libphp-serialization-perl + ,libmodule-load-conditional-perl + ,libnet-sftp-foreign-perl + ,libarchive-zip-perl + ,libdbd-mysql-perl + ,libdevice-serialport-perl + ,libimage-info-perl + ,libjson-maybexs-perl + ,libsys-mmap-perl [!hurd-any] + ,liburi-encode-perl + ,libwww-perl, liburi-perl + ,libdata-dump-perl + ,libdatetime-perl + ,libclass-std-fast-perl + ,libsoap-wsdl-perl + ,libio-socket-multicast-perl + ,libdigest-sha-perl + ,libsys-cpu-perl, libsys-meminfo-perl + ,libdata-uuid-perl + ,libnumber-bytes-human-perl + ,libfile-slurp-perl + ,mysql-client | mariadb-client | virtual-mysql-client + ,perl-modules + ,php-mysql, php-gd, php-apcu, php-apc | php-apcu-bc, php-json + ,policykit-1 + ,rsyslog | system-log-daemon + ,zip + ,libpcre3 + ,libcrypt-eksblowfish-perl + ,libdata-entropy-perl +Recommends: ${misc:Recommends} + ,libapache2-mod-php | php-fpm + ,mysql-server | mariadb-server | virtual-mysql-server + ,zoneminder-doc (>= ${source:Version}) + ,ffmpeg +Suggests: fcgiwrap, logrotate +Description: video camera security and surveillance solution + ZoneMinder is intended for use in single or multi-camera video security + applications, including commercial or home CCTV, theft prevention and child + or family member or home monitoring and other care scenarios. It + supports capture, analysis, recording, and monitoring of video data coming + from one or more video or network cameras attached to a Linux system. + ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom + cameras using a variety of protocols. It is suitable for use as a home + video security system and for commercial or professional video security + and surveillance. It can also be integrated into a home automation system + via X.10 or other protocols. + +#Package: libzoneminder-perl +#Section: perl +#Architecture: all +#Multi-Arch: foreign +#Depends: ${misc:Depends}, ${perl:Depends} +# ,libarchive-zip-perl +# ,libdbd-mysql-perl +# ,libdevice-serialport-perl +# ,libimage-info-perl +# ,libjson-maybexs-perl +# ,libsys-mmap-perl [!hurd-any] +# ,liburi-encode-perl +# ,libwww-perl +#Description: ZoneMinder Perl libraries +# ZoneMinder is intended for use in single or multi-camera video security +# applications, including commercial or home CCTV, theft prevention and child +# or family member or home monitoring and other care scenarios. It +# supports capture, analysis, recording, and monitoring of video data coming +# from one or more video or network cameras attached to a Linux system. +# ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom +# cameras using a variety of protocols. It is suitable for use as a home +# video security system and for commercial or professional video security +# and surveillance. It can also be integrated into a home automation system +# via X.10 or other protocols. +# . +# This package provides ZoneMinder Perl libraries; it can be used to +# write custom interfaces as well. + +Package: zoneminder-doc +Section: doc +Architecture: all +Multi-Arch: foreign +Depends: ${misc:Depends}, ${sphinxdoc:Depends}, python-sphinx-rtd-theme | python3-sphinx-rtd-theme +Suggests: www-browser +Description: ZoneMinder documentation + ZoneMinder is intended for use in single or multi-camera video security + applications, including commercial or home CCTV, theft prevention and child + or family member or home monitoring and other care scenarios. It + supports capture, analysis, recording, and monitoring of video data coming + from one or more video or network cameras attached to a Linux system. + ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom + cameras using a variety of protocols. It is suitable for use as a home + video security system and for commercial or professional video security + and surveillance. It can also be integrated into a home automation system + via X.10 or other protocols. + . + This package provides ZoneMinder documentation in HTML format. + +Package: zoneminder-dbg +Section: debug +Priority: extra +Architecture: any +Depends: zoneminder (= ${binary:Version}), ${misc:Depends} +Description: Zoneminder -- debugging symbols + ZoneMinder is intended for use in single or multi-camera video security + applications, including commercial or home CCTV, theft prevention and child + or family member or home monitoring and other care scenarios. It + supports capture, analysis, recording, and monitoring of video data coming + from one or more video or network cameras attached to a Linux system. + ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom + cameras using a variety of protocols. It is suitable for use as a home + video security system and for commercial or professional video security + and surveillance. It can also be integrated into a home automation system + via X.10 or other protocols. + . + This package provides debugging symbols diff --git a/distros/ubuntu2004/copyright b/distros/ubuntu2004/copyright new file mode 100644 index 000000000..c48025a25 --- /dev/null +++ b/distros/ubuntu2004/copyright @@ -0,0 +1,174 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: ZoneMinder +Upstream-Contact: Philip Coombes +Source: https://github.com/ZoneMinder/ZoneMinder +Comment: + This package was originally debianized by matrix + on Mon, 7 Mar 2005 02:07:57 -0500. + It was re-done for submission to the Debian project by Peter Howard + on Fri, 8 Dec 2006 10:19:43 +1100 +Files-Excluded: + web/skins/*/js/jquery-* + web/tools/mootools/*-yc.js + +Files: * +Copyright: 2001-2014 Philip Coombes + 2008 Brian Rudy + 2014 Vincent Giovannone + 2013 Tim Craig + 2003-2008 Corey DeLasaux + 2001-2010 Chris Kistner +License: GPL-2+ + +Files: distros/* +Copyright: 2001-2008 Philip Coombes + 2014 Isaac Connor + 2005 Serg Oskin +License: GPL-2+ + +Files: web/skins/*/js/jquery-* +Copyright: 2010 John Resig + 2010 The Dojo Foundation +License: GPL-2 or Expat +Comment: + Dual licensed under the MIT or GPL Version 2 licenses. + http://jquery.org/license + . + Includes Sizzle.js http://sizzlejs.com/ + Released under the MIT, BSD, and GPL Licenses. + +Files: web/tools/mootools/*.js +Copyright: 2009 Marcelo Jorge Vieira (metal) + 2006-2010 Valerio Proietti (http://mad4milk.net/) +License: Expat + +Files: web/api/* +Copyright: 2005-2013 Cake Software Foundation, Inc. (http://cakefoundation.org) +License: Expat + +Files: + cmake/Modules/CheckPrototypeDefinition*.cmake + cmake/Modules/FindGLIB2.cmake + cmake/Modules/FindPolkit.cmake + cmake/Modules/GNUInstallDirs.cmake +Copyright: + 2005-2011 Kitware, Inc. + 2010-2011 Andreas Schneider + 2009 Dario Freddi + 2008 Laurent Montel, + 2011 Nikita Krupen'ko +License: BSD-3-clause + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + . + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + . + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + . + * The names of Kitware, Inc., the Insight Consortium, or the names of + any consortium members, or of any contributors, may not be used to + endorse or promote products derived from this software without + specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Files: cmake/Modules/FindPerlModules.cmake +Copyright: 2012 Iowa State University +License: Boost-1.0 + Boost Software License - Version 1.0 - August 17th, 2003 + . + Permission is hereby granted, free of charge, to any person or organization + obtaining a copy of the software and accompanying documentation covered by + this license (the "Software") to use, reproduce, display, distribute, + execute, and transmit the Software, and to prepare derivative works of the + Software, and to permit third-parties to whom the Software is furnished to + do so, all subject to the following: + . + The copyright notices in the Software and this entire statement, including + the above license grant, this restriction and the following disclaimer, + must be included in all copies of the Software, in whole or in part, and + all derivative works of the Software, unless such copies or derivative + works are solely in the form of machine-executable object code generated by + a source language processor. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + +Files: debian/* +Copyright: 2015 Dmitry Smirnov + 2007-2014 Peter Howard + 2010-2012 Vagrant Cascadian + 2001-2008 Philip Coombes +License: GPL-2+ + +License: Expat + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +License: GPL-2+ + This package 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 package 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 package; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + . + The complete text of the GNU General Public License version 2 + can be found in "/usr/share/common-licenses/GPL-2". + +License: GPL-2 + This package 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; version 2 of the License. + . + This package 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 package; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + . + The complete text of the GNU General Public License version 2 + can be found in "/usr/share/common-licenses/GPL-2". diff --git a/distros/ubuntu2004/examples/nginx.conf b/distros/ubuntu2004/examples/nginx.conf new file mode 100644 index 000000000..5636ca3e1 --- /dev/null +++ b/distros/ubuntu2004/examples/nginx.conf @@ -0,0 +1,32 @@ +location /zm/cgi-bin { + gzip off; + alias /usr/lib/zoneminder/cgi-bin; + + include /etc/nginx/fastcgi_params; + fastcgi_param SCRIPT_FILENAME $request_filename; + fastcgi_pass unix:/var/run/fcgiwrap.socket; +} + +location /zm { +# if ($scheme ~ ^http:){ +# rewrite ^(.*)$ https://$host$1 permanent; +# } + + gzip off; + alias /usr/share/zoneminder/www; + index index.php; + + location ~ \.php$ { + if (!-f $request_filename) { return 404; } + expires epoch; + include /etc/nginx/fastcgi_params; + fastcgi_param SCRIPT_FILENAME $request_filename; + fastcgi_index index.php; + fastcgi_pass unix:/var/run/php5-fpm.sock; + } + + location ~ \.(jpg|jpeg|gif|png|ico)$ { + access_log off; + expires 33d; + } +} diff --git a/distros/ubuntu2004/gbp.conf b/distros/ubuntu2004/gbp.conf new file mode 100644 index 000000000..4608913d9 --- /dev/null +++ b/distros/ubuntu2004/gbp.conf @@ -0,0 +1,7 @@ + +[dch] +id-length = 0 + +[import-orig] +pristine-tar = False +merge = False diff --git a/distros/ubuntu2004/libzoneminder-perl.install b/distros/ubuntu2004/libzoneminder-perl.install new file mode 100644 index 000000000..67191d9cf --- /dev/null +++ b/distros/ubuntu2004/libzoneminder-perl.install @@ -0,0 +1,2 @@ +usr/share/man/man3 +usr/share/perl5 diff --git a/distros/ubuntu1410/patches/series b/distros/ubuntu2004/patches/series similarity index 100% rename from distros/ubuntu1410/patches/series rename to distros/ubuntu2004/patches/series diff --git a/distros/ubuntu2004/rules b/distros/ubuntu2004/rules new file mode 100755 index 000000000..5a1cdb500 --- /dev/null +++ b/distros/ubuntu2004/rules @@ -0,0 +1,96 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +export DEB_BUILD_MAINT_OPTIONS = hardening=+all +export DEB_LDFLAGS_MAINT_APPEND += -Wl,--as-needed + +ifeq ($(DEB_BUILD_ARCH_OS),hurd) +ARGS:= -DZM_NO_MMAP=ON +endif + +%: + dh $@ --parallel --buildsystem=cmake --builddirectory=dbuild \ + --with systemd,sphinxdoc,apache2,linktree + +override_dh_auto_configure: + dh_auto_configure -- $(ARGS) \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DZM_CONFIG_DIR="/etc/zm" \ + -DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \ + -DZM_RUNDIR="/run/zm" \ + -DZM_SOCKDIR="/run/zm" \ + -DZM_TMPDIR="/var/tmp/zm" \ + -DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \ + -DZM_CACHEDIR="/var/cache/zoneminder/cache" \ + -DZM_DIR_EVENTS="/var/cache/zoneminder/events" \ + -DZM_PATH_SHUTDOWN="/sbin/shutdown" \ + -DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" + +override_dh_clean: + dh_clean $(MANPAGES1) + $(RM) -r docs/_build + +build-indep: + #$(MAKE) -C docs text + $(MAKE) -C docs html + +MANPAGES1 = dbuild/scripts/zmupdate.pl.1 +$(MANPAGES1): + # generate man page(s): + pod2man -s1 --stderr --utf8 $(patsubst %.1, %, $@) $@ + +## reproducible build: +LAST_CHANGE=$(shell dpkg-parsechangelog -S Date) +BUILD_DATE=$(shell LC_ALL=C date -u "+%B %d, %Y" -d "$(LAST_CHANGE)") +override_dh_installman: $(MANPAGES1) + $(MAKE) -C docs man SPHINXOPTS="-D today=\"$(BUILD_DATE)\"" + dh_installman --language=C $(MANPAGES1) + +override_dh_auto_install: + dh_auto_install --destdir=$(CURDIR)/debian/tmp + # remove worthless files: + $(RM) -v $(CURDIR)/debian/tmp/usr/share/perl5/*/*/*/.packlist + $(RM) -v $(CURDIR)/debian/tmp/usr/share/perl5/*/*.in + # remove empty directories: + find $(CURDIR)/debian/tmp/usr -type d -empty -delete -printf 'removed %p\n' + # remove extra-license-file: + $(RM) -v $(CURDIR)/debian/tmp/usr/share/zoneminder/www/api/lib/Cake/LICENSE.txt + +override_dh_fixperms: + dh_fixperms + # + # As requested by the Debian Webapps Policy Manual §3.2.1 + chown root:www-data $(CURDIR)/debian/zoneminder/etc/zm/zm.conf + chmod 640 $(CURDIR)/debian/zoneminder/etc/zm/zm.conf + +override_dh_systemd_start: + dh_systemd_start --no-start + +override_dh_systemd_enable: + dh_systemd_enable --no-enable + +override_dh_apache2: + dh_apache2 --noenable + +override_dh_strip: + [ -d "$(CURDIR)/debian/zoneminder-dbg" ] \ + && dh_strip --dbg-package=zoneminder-dbg \ + || dh_strip + +#%: +# dh $@ --parallel --buildsystem=autoconf --with autoreconf +# +#override_dh_auto_configure: +# dh_auto_configure -- \ +# --sysconfdir=/etc/zm \ +# --with-mysql=/usr \ +# --with-webdir=/usr/share/zoneminder \ +# --with-ffmpeg=/usr \ +# --with-cgidir=/usr/lib/cgi-bin \ +# --with-webuser=www-data \ +# --with-webgroup=www-data \ +# --enable-mmap=yes diff --git a/distros/ubuntu2004/source/format b/distros/ubuntu2004/source/format new file mode 100644 index 000000000..163aaf8d8 --- /dev/null +++ b/distros/ubuntu2004/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/distros/ubuntu2004/source/lintian-overrides b/distros/ubuntu2004/source/lintian-overrides new file mode 100644 index 000000000..3669e5de8 --- /dev/null +++ b/distros/ubuntu2004/source/lintian-overrides @@ -0,0 +1,9 @@ +## Actually sources are there: "*-nc.js". +source-is-missing web/tools/mootools/mootools-*-yc.js + +## We're using "libjs-jquery" instead. +source-is-missing web/skins/*/js/jquery-1.4.2.min.js + +## Acknowledged, will repack eventually. +source-contains-prebuilt-javascript-object web/tools/mootools/mootools-*-yc.js +source-contains-prebuilt-javascript-object web/skins/*/js/jquery-1.4.2.min.js diff --git a/distros/ubuntu2004/zoneminder-doc.doc-base b/distros/ubuntu2004/zoneminder-doc.doc-base new file mode 100644 index 000000000..c43dc4336 --- /dev/null +++ b/distros/ubuntu2004/zoneminder-doc.doc-base @@ -0,0 +1,8 @@ +Document: zoneminder-doc +Title: Zoneminder documentation +Abstract: This document describes how to use Zoneminder. +Section: System/Administration + +Format: HTML +Index: /usr/share/doc/zoneminder-doc/html/index.html +Files: /usr/share/doc/zoneminder-doc/html/* diff --git a/distros/ubuntu2004/zoneminder-doc.install b/distros/ubuntu2004/zoneminder-doc.install new file mode 100644 index 000000000..c19bc6f3a --- /dev/null +++ b/distros/ubuntu2004/zoneminder-doc.install @@ -0,0 +1 @@ +docs/_build/html usr/share/doc/zoneminder-doc/ diff --git a/distros/ubuntu2004/zoneminder-doc.links b/distros/ubuntu2004/zoneminder-doc.links new file mode 100644 index 000000000..cc09f6462 --- /dev/null +++ b/distros/ubuntu2004/zoneminder-doc.links @@ -0,0 +1,2 @@ +## Convenience symlink: +/usr/share/doc/zoneminder-doc/html /usr/share/doc/zoneminder/html diff --git a/distros/ubuntu2004/zoneminder.apache2 b/distros/ubuntu2004/zoneminder.apache2 new file mode 100644 index 000000000..466144fa7 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.apache2 @@ -0,0 +1 @@ +conf debian/conf/apache2/zoneminder.conf nginx diff --git a/distros/ubuntu2004/zoneminder.bug-presubj b/distros/ubuntu2004/zoneminder.bug-presubj new file mode 100644 index 000000000..990fc1d94 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.bug-presubj @@ -0,0 +1,5 @@ +Unless bug is specific to Debian please consider reporting it directly to +upstream developer(s): + + https://github.com/ZoneMinder/ZoneMinder/issues + diff --git a/distros/ubuntu1410/zoneminder-core.dirs b/distros/ubuntu2004/zoneminder.dirs similarity index 52% rename from distros/ubuntu1410/zoneminder-core.dirs rename to distros/ubuntu2004/zoneminder.dirs index 350c32aff..6db3d5a95 100644 --- a/distros/ubuntu1410/zoneminder-core.dirs +++ b/distros/ubuntu2004/zoneminder.dirs @@ -1,4 +1,9 @@ var/log/zm +var/lib/zm var/cache/zoneminder/events var/cache/zoneminder/images var/cache/zoneminder/temp +var/cache/zoneminder/cache +usr/share/zoneminder/db +etc/zm/ +etc/zm/conf.d diff --git a/distros/ubuntu1410/docs b/distros/ubuntu2004/zoneminder.docs similarity index 100% rename from distros/ubuntu1410/docs rename to distros/ubuntu2004/zoneminder.docs diff --git a/distros/ubuntu2004/zoneminder.examples b/distros/ubuntu2004/zoneminder.examples new file mode 100644 index 000000000..3b8befe7b --- /dev/null +++ b/distros/ubuntu2004/zoneminder.examples @@ -0,0 +1,2 @@ +debian/examples/* +dbuild/misc/apache.conf diff --git a/distros/ubuntu1410/zoneminder-core.zoneminder.init b/distros/ubuntu2004/zoneminder.init similarity index 78% rename from distros/ubuntu1410/zoneminder-core.zoneminder.init rename to distros/ubuntu2004/zoneminder.init index d3354c1d8..6132481f3 100644 --- a/distros/ubuntu1410/zoneminder-core.zoneminder.init +++ b/distros/ubuntu2004/zoneminder.init @@ -8,23 +8,24 @@ # 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 -# description: Control ZoneMinder as a Service # chkconfig: 2345 20 20 # Source function library. -#. /etc/rc.d/init.d/functions +. /lib/lsb/init-functions prog=ZoneMinder ZM_PATH_BIN="/usr/bin" -RUNDIR=/var/run/zm -TMPDIR=/tmp/zm +RUNDIR="/run/zm" +TMPDIR="/tmp/zm" command="$ZM_PATH_BIN/zmpkg.pl" start() { echo -n "Starting $prog: " - mkdir -p $RUNDIR && chown www-data:www-data $RUNDIR - mkdir -p $TMPDIR && chown www-data:www-data $TMPDIR + 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 @@ -37,11 +38,11 @@ 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 + # 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 . . . + # uninstalling . . . # result=`$command status` if [ ! "$result" = "running" ]; then diff --git a/distros/ubuntu2004/zoneminder.install b/distros/ubuntu2004/zoneminder.install new file mode 100644 index 000000000..67b135de5 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.install @@ -0,0 +1,11 @@ +etc/zm/zm.conf +etc/zm/conf.d/* +usr/bin +usr/lib/zoneminder +usr/share/polkit-1 +usr/share/zoneminder/db +usr/share/zoneminder/www + +# libzoneminder-perl files: +usr/share/man/man3 +usr/share/perl5 diff --git a/distros/ubuntu2004/zoneminder.links b/distros/ubuntu2004/zoneminder.links new file mode 100644 index 000000000..b7258c3c4 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.links @@ -0,0 +1 @@ +/var/tmp /usr/share/zoneminder/www/api/app/tmp diff --git a/distros/ubuntu2004/zoneminder.linktrees b/distros/ubuntu2004/zoneminder.linktrees new file mode 100644 index 000000000..2e843bbf1 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.linktrees @@ -0,0 +1,14 @@ +## cakephp +#replace /usr/share/php/Cake /usr/share/zoneminder/www/api/lib/Cake + +## libjs-mootools +replace /usr/share/javascript/mootools/mootools.js /usr/share/zoneminder/www/tools/mootools/mootools-core.js +replace /usr/share/javascript/mootools/mootools.js /usr/share/zoneminder/www/tools/mootools/mootools-core-1.3.2-nc.js +replace /usr/share/javascript/mootools/mootools.js /usr/share/zoneminder/www/tools/mootools/mootools-core-1.3.2-yc.js +replace /usr/share/javascript/mootools/mootools-more.js /usr/share/zoneminder/www/tools/mootools/mootools-more.js +replace /usr/share/javascript/mootools/mootools-more.js /usr/share/zoneminder/www/tools/mootools/mootools-more-1.3.2.1-nc.js +replace /usr/share/javascript/mootools/mootools-more.js /usr/share/zoneminder/www/tools/mootools/mootools-more-1.3.2.1-yc.js + +## libjs-jquery +replace /usr/share/javascript/jquery/jquery.min.js /usr/share/zoneminder/www/skins/classic/js/jquery-1.4.2.min.js +replace /usr/share/javascript/jquery/jquery.min.js /usr/share/zoneminder/www/skins/flat/js/jquery-1.4.2.min.js diff --git a/distros/ubuntu2004/zoneminder.lintian-overrides b/distros/ubuntu2004/zoneminder.lintian-overrides new file mode 100644 index 000000000..90be05a9f --- /dev/null +++ b/distros/ubuntu2004/zoneminder.lintian-overrides @@ -0,0 +1,14 @@ +# Depends: policykit-1 +unusual-interpreter usr/bin/zmsystemctl.pl #!/usr/bin/pkexec + +# Intentionally not others-readable, #637685. +non-standard-file-perm etc/zm/zm.conf 0640 != 0644 + +# Bundled Cake PHP framework, not intended for direct execution: +script-not-executable usr/share/zoneminder/www/api/* + +# Annoying but seems to be too much troubles to fix; should be fixed upstream: +script-with-language-extension usr/bin/*.pl + +# dh-linktree: +package-contains-broken-symlink usr/share/zoneminder/www/api/lib/Cake/* diff --git a/distros/ubuntu2004/zoneminder.logrotate b/distros/ubuntu2004/zoneminder.logrotate new file mode 100644 index 000000000..6162e9c4d --- /dev/null +++ b/distros/ubuntu2004/zoneminder.logrotate @@ -0,0 +1,13 @@ +/var/log/zm/*.log { + missingok + notifempty + sharedscripts + delaycompress + compress + postrotate + /usr/bin/zmpkg.pl logrot >>/dev/null 2>&1 || : + endscript + daily + rotate 7 + maxage 7 +} diff --git a/distros/ubuntu2004/zoneminder.maintscript b/distros/ubuntu2004/zoneminder.maintscript new file mode 100644 index 000000000..3aa20b3a0 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.maintscript @@ -0,0 +1 @@ +rm_conffile /etc/zm/apache.conf 1.28.1-5~ diff --git a/distros/ubuntu2004/zoneminder.manpages b/distros/ubuntu2004/zoneminder.manpages new file mode 100644 index 000000000..d2053d688 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.manpages @@ -0,0 +1 @@ +docs/_build/man/*.1 diff --git a/distros/ubuntu2004/zoneminder.postinst b/distros/ubuntu2004/zoneminder.postinst new file mode 100644 index 000000000..39a2fa1b0 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.postinst @@ -0,0 +1,131 @@ +#! /bin/sh + +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 + if [ -z "$2" ]; then + chown www-data:www-data /var/cache/zoneminder /var/cache/zoneminder/* + fi + if [ ! -e "/etc/apache2/mods-enabled/cgi.load" ] && [ "$(command -v a2enmod)" != "" ]; then + echo "The cgi module is not enabled in apache2. I am enabling it using a2enmod cgi." + 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 [ $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 + DBSERVICE="mysql.service" + fi + echo "Detected db service is $DBSERVICE" + if systemctl is-failed --quiet $DBSERVICE; then + echo "$DBSERVICE is in a failed state; it will not be started." + echo "If you have already resolved the problem preventing $DBSERVICE from running," + 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 + 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 + fi + +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/distros/ubuntu2004/zoneminder.postrm b/distros/ubuntu2004/zoneminder.postrm new file mode 100644 index 000000000..ba2066c8d --- /dev/null +++ b/distros/ubuntu2004/zoneminder.postrm @@ -0,0 +1,14 @@ +#! /bin/sh + +set -e + +if [ "$1" = "purge" ]; then + echo " +Reminder: to completely remove \"zoneminder\" it may be necessary + * to delete database using the following sample command: + sudo mysqladmin --defaults-file=/etc/mysql/debian.cnf -f drop zm + * to delete remaining data files in "/var/cache/zoneminder". +" +fi + +#DEBHELPER# diff --git a/distros/ubuntu2004/zoneminder.preinst b/distros/ubuntu2004/zoneminder.preinst new file mode 100644 index 000000000..6088c3ea9 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.preinst @@ -0,0 +1,11 @@ +#!/bin/sh + +set -e + +## Remove obsolete symlink which is in the way of dh_apache2: +ol="/etc/apache2/conf-available/zoneminder.conf" +if [ -h "${ol}" ]; then + [ "$(readlink ${ol})" = "/etc/zm/apache.conf" ] && rm -f "${ol}" +fi + +#DEBHELPER# diff --git a/distros/ubuntu1204/zoneminder.service b/distros/ubuntu2004/zoneminder.service similarity index 95% rename from distros/ubuntu1204/zoneminder.service rename to distros/ubuntu2004/zoneminder.service index ac719b733..cb2d6791e 100644 --- a/distros/ubuntu1204/zoneminder.service +++ b/distros/ubuntu2004/zoneminder.service @@ -13,7 +13,7 @@ Type=forking ExecStart=/usr/bin/zmpkg.pl start ExecReload=/usr/bin/zmpkg.pl restart ExecStop=/usr/bin/zmpkg.pl stop -PIDFile=/var/run/zm/zm.pid +PIDFile=/run/zm/zm.pid Restart=always RestartSec=10 Environment=TZ=:/etc/localtime diff --git a/distros/ubuntu2004/zoneminder.tmpfile b/distros/ubuntu2004/zoneminder.tmpfile new file mode 100644 index 000000000..cbfdec1de --- /dev/null +++ b/distros/ubuntu2004/zoneminder.tmpfile @@ -0,0 +1,4 @@ +d /run/zm 0755 www-data www-data +d /tmp/zm 0755 www-data www-data +d /var/tmp/zm 0755 www-data www-data +d /var/cache/zoneminder/cache 0755 www-data www-data diff --git a/docs/_static/Icon_External_Link.png b/docs/_static/Icon_External_Link.png new file mode 100644 index 000000000..16f9b92db Binary files /dev/null and b/docs/_static/Icon_External_Link.png differ diff --git a/docs/_static/README.txt b/docs/_static/README.txt new file mode 100644 index 000000000..acc1d59d9 --- /dev/null +++ b/docs/_static/README.txt @@ -0,0 +1,2 @@ +The external link icon is GPL licensed. Original license link: +https://commons.wikimedia.org/wiki/Category:External_link_icons#/media/File:Icon_External_Link.png diff --git a/docs/_static/zmstyle.css b/docs/_static/zmstyle.css index b8f0235f3..986614fa6 100644 --- a/docs/_static/zmstyle.css +++ b/docs/_static/zmstyle.css @@ -1,3 +1,18 @@ img { border: 1px solid black !important; } + +.admonition-todo { + border-top: 2px solid red; + border-bottom: 2px solid red; + border-left: 2px solid red; + border-right: 2px solid red; + background-color: #ff6347; + } + + a.reference.external:link, + a.reference.external:visited{ + background: url('./Icon_External_Link.png') center right no-repeat; + padding-right: 13px; + + } \ No newline at end of file diff --git a/docs/api.rst b/docs/api.rst index 177678977..23effc0b7 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -14,6 +14,10 @@ The API is built in CakePHP and lives under the ``/api`` directory. It provides a RESTful service and supports CRUD (create, retrieve, update, delete) functions for Monitors, Events, Frames, Zones and Config. +API Wrappers +^^^^^^^^^^^^^ +- pyzm is a python wrapper for the ZoneMinder APIs. It supports both the legacy and new token based API, as well as ZM logs/ZM shared memory support. See `its project site `__ for more details. Documentation is `here `__. + API evolution ^^^^^^^^^^^^^^^ @@ -21,12 +25,14 @@ The ZoneMinder API has evolved over time. Broadly speaking the iterations were a * Prior to version 1.29, there really was no API layer. Users had to use the same URLs that the web console used to 'mimic' operations, or use an XML skin * Starting version 1.29, a v1.0 CakePHP based API was released which continues to evolve over time. From a security perspective, it still tied into ZM auth and required client cookies for many operations. Primarily, two authentication modes were offered: - * You use cookies to maintain session state (`ZM_SESS_ID`) - * You use an authentication hash to validate yourself, which included encoding personal information and time stamps which at times caused timing validation issues, especially for mobile consumers -* Starting version 1.34, ZoneMinder has introduced a new "token" based system which is based JWT. We have given it a '2.0' version ID. These tokens don't encode any personal data and can be statelessly passed around per request. It introduces concepts like access tokens, refresh tokens and per user level API revocation to manage security better. The internal components of ZoneMinder all support this new scheme now and if you are using the APIs we strongly recommend you migrate to 1.34 and use this new token system (as a side note, 1.34 also moves from MYSQL PASSWORD to Bcrypt for passwords, which is also a good reason why you should migate). -* Note that as of 1.34, both versions of API access will work (tokens and the older auth hash mechanism). -.. NOTE:: + * You use cookies to maintain session state (``ZM_SESS_ID``) + * You use an authentication hash to validate yourself, which included encoding personal information and time stamps which at times caused timing validation issues, especially for mobile consumers + +* Starting version 1.34, ZoneMinder has introduced a new "token" based system which is based JWT. We have given it a '2.0' version ID. These tokens don't encode any personal data and can be statelessly passed around per request. It introduces concepts like access tokens, refresh tokens and per user level API revocation to manage security better. The internal components of ZoneMinder all support this new scheme now and if you are using the APIs we strongly recommend you migrate to 1.34 and use this new token system (as a side note, 1.34 also moves from MYSQL PASSWORD to Bcrypt for passwords, which is also a good reason why you should migate). +* Note that as of 1.34, both versions of API access will work (tokens and the older auth hash mechanism), however we no longer use sessions by default. You will have to add a ``stateful=1`` query parameter during login to tell ZM to set a COOKIE and store the required info in the session. This option is only available if ``OPT_USE_LEGACY_API_AUTH`` is set to ON. + +.. note:: For the rest of the document, we will specifically highlight v2.0 only features. If you don't see a special mention, assume it applies for both API versions. @@ -35,7 +41,9 @@ Enabling API ^^^^^^^^^^^^^ ZoneMinder comes with APIs enabled. To check if APIs are enabled, visit ``Options->System``. If ``OPT_USE_API`` is enabled, your APIs are active. -For v2.0 APIs, you have an additional option right below it - ``OPT_USE_LEGACY_API_AUTH`` which is enabled by default. When enabled, the `login.json` API (discussed later) will return both the old style (``auth=``) and new style (``token=``) credentials. The reason this is enabled by default is because any existing apps that use the API would break if they were not updated to use v2.0. (Note that zmNinja 1.3.057 and beyond will support tokens) +For v2.0 APIs, you have an additional option right below it: + + * ``OPT_USE_LEGACY_API_AUTH`` which is enabled by default. When enabled, the `login.json` API (discussed later) will return both the old style (``auth=``) and new style (``token=``) credentials. The reason this is enabled by default is because any existing apps that use the API would break if they were not updated to use v2.0. (Note that zmNinja 1.3.057 and beyond will support tokens) Enabling secret key ^^^^^^^^^^^^^^^^^^^ @@ -51,10 +59,14 @@ To get an API key: :: - curl -XPOST [-c cookies.txt] -d "user=yourusername&pass=yourpassword" https://yourserver/zm/api/host/login.json + curl -XPOST -d "user=yourusername&pass=yourpassword" https://yourserver/zm/api/host/login.json -The ``[-c cookies.txt]`` is optional, and will be explained in the next section. +If you want to use a stateful connection, so you don't have to pass auth credentials with each query, you can use the following: + +:: + + curl -XPOST -c cookies.txt -d "user=yourusername&pass=yourpassword&stateful=1" https://yourserver/zm/api/host/login.json This returns a payload like this for API v1.0: @@ -89,17 +101,14 @@ Once you have the keys (a.k.a credentials (v1.0, v2.0) or token (v2.0)) you shou :: + # v1.0 or 2.0 based API access (will only work if AUTH_HASH_LOGINS is enabled + # RECOMMENDED: v2.0 token based - curl -XPOST https://yourserver/zm/api/monitors.json&token= + curl -XGET https://yourserver/zm/api/monitors.json?token= - # or + # or, for legacy mode: - # v1.0 or 2.0 based API access (will only work if AUTH_HASH_LOGINS is enabled) - curl -XPOST -d "auth=" https://yourserver/zm/api/monitors.json - - # or - - curl -XGET https://yourserver/zm/api/monitors.json&auth= + curl -XGET https://yourserver/zm/api/monitors.json?auth= # or, if you specified -c cookies.txt in the original login request @@ -107,8 +116,8 @@ Once you have the keys (a.k.a credentials (v1.0, v2.0) or token (v2.0)) you shou .. NOTE:: - ZoneMinder's API layer allows API keys to be encoded either as a query parameter or as a data payload. If you don't pass keys, you could use cookies (not recommended as a general approach) + If you are using an ``HTTP GET`` request, the token/auth needs to be passed as a query parameter in the URL. If you are using an ``HTTP POST`` (like when you use the API to modify a monitor, for example), you can choose to pass the token as a data payload instead. The API layer discards data payloads for ``HTTP GET``. Finally, If you don't pass keys, you could also use cookies (not recommended as a general approach). Key lifetime (v1.0) ^^^^^^^^^^^^^^^^^^^^^ @@ -118,7 +127,7 @@ If you are using the old credentials mechanism present in v1.0, then the credent Key lifetime (v2.0) ^^^^^^^^^^^^^^^^^^^^^^ -In version 2.0, it is easy to know when a key will expire before you use it. You can find that out from the ``access_token_expires`` and ``refresh_token_exipres`` values (in seconds) after you decode the JWT key (there are JWT decode libraries for every language you want). You should refresh the keys before the timeout occurs, or you will not be able to use the APIs. +In version 2.0, it is easy to know when a key will expire before you use it. You can find that out from the ``access_token_expires`` and ``refresh_token_expires`` values (in seconds) after you decode the JWT key (there are JWT decode libraries for every language you want). You should refresh the keys before the timeout occurs, or you will not be able to use the APIs. Understanding access/refresh tokens (v2.0) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -473,6 +482,7 @@ Create a Zone &Zone[Units]=Percent\ &Zone[NumCoords]=4\ &Zone[Coords]=0,0 639,0 639,479 0,479\ + &Zone[Area]=307200\ &Zone[AlarmRGB]=16711680\ &Zone[CheckMethod]=Blobs\ &Zone[MinPixelThreshold]=25\ diff --git a/docs/conf.py b/docs/conf.py index 9bda5df7b..9de7bd820 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -31,7 +31,7 @@ def setup(app): # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [] +extensions = ['sphinx.ext.todo'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -265,3 +265,6 @@ texinfo_documents = [ # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False + +# Display todos by setting to True +todo_include_todos = True diff --git a/docs/faq.rst b/docs/faq.rst index 5b8c97f5c..56b67d082 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -1,8 +1,11 @@ FAQ ===== +.. todo:: needs to be reviewed - some entries may be old/invalid. I've done one round, but icOn needs to review. + This is the FAQ page. Feel free to contribute any FAQs that you think are missing. +.. note:: It is always a good idea to refer to the `ZoneMinder forums `__ for tips and tricks. While we try and make sure this FAQ is pruned/adjusted to align with the latest stable release, some of the entries may no longer be accurate (or there may be better suggestions in the forums). How can I stop ZoneMinder filling up my disk? --------------------------------------------- @@ -29,23 +32,22 @@ To delete events that are older than 7 days, create a new filter with "Date" set Save with 'Run Filter In Background' enabled to have it run automatically. Optional skip archived events: click on the plus sign next to -7 days to add another condition. "and" "archive status" equal to "unarchived only". -Optional slow delete: limit the number of results to 3. If you have a large backlog of events that would be deleted, this can hard spike the CPU usage for a long time. Limiting the number of results to only the first three each time the filter is run spreads out the delete processes over time, dramatically lessening the CPU load. +Optional slow delete: limit the number of results to a number, say ``10`` in the filter. If you have a large backlog of events that would be deleted, this can hard spike the CPU usage for a long time. Limiting the number of results to only the first three each time the filter is run spreads out the delete processes over time, dramatically lessening the CPU load. + + +.. warning:: We no longer recommend use enable ``OPT_FAST_DELETE`` or ``RUN_AUDIT`` anymore, unless you are using an old or low powered system to run Zoneminder. Please consider the remaining tips in this answer to be 'generally deprecated, use only if you must'. There are two methods for ZM to remove files when they are deleted that can be found in Options under the System tab ZM_OPT_FAST_DELETE and ZM_RUN_AUDIT. ZM_OPT_FAST_DELETE: -Normally an event created as the result of an alarm consists of entries in one or more database tables plus the various files associated with it. When deleting events in the browser it can take a long time to remove all of this if you are trying to do a lot of events at once. If you are running on an older or under-powered system, you may want to set this option which means that the browser client only deletes the key entries in the events table, which means the events will no longer appear in the listing, and leaves the zmaudit daemon to clear up the rest later. If you do so, disk space will not be freed immediately so you will need to run zmaudit more frequently. On modern systems, we recommend that you leave this off. - - +Normally an event created as the result of an alarm consists of entries in one or more database tables plus the various files associated with it. When deleting events in the browser it can take a long time to remove all of this if you are trying to do a lot of events at once. If you are running on an older or under-powered system, you may want to set this option which means that the browser client only deletes the key entries in the events table, which means the events will no longer appear in the listing, and leaves the zmaudit daemon to clear up the rest later. If you do so, disk space will not be freed immediately so you will need to run zmaudit more frequently. On modern systems, we recommend that you leave this **off**. ZM_RUN_AUDIT: The zmaudit daemon exists to check that the saved information in the database and on the file system match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronize the two data stores. This option controls whether zmaudit is run in the background and performs these checks and fixes continuously. This is recommended for most systems however if you have a very large number of events the process of scanning the database and file system may take a long time and impact performance. In this case you may prefer to not have zmaudit running unconditionally and schedule occasional checks at other, more convenient, times. - - ZM_AUDIT_CHECK_INTERVAL: The zmaudit daemon exists to check that the saved information in the database and on the files system match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronize the two data stores. The default check interval of 900 seconds (15 minutes) is fine for most systems however if you have a very large number of events the process of scanning the database and file system may take a long time and impact performance. In this case you may prefer to make this interval much larger to reduce the impact on your system. This option determines how often these checks are performed. @@ -64,12 +66,15 @@ In *general* a good estimate of memory required would be: Min Bits of Memory = 20% overhead * (image-width*image-height*image buffer size*target color space*number of cameras) Where: + * image-width and image-height are the width and height of images that your camera is configured for (in my case, 1280x960). This value is in the Source tab for each monitor * image buffer size is the # of images ZM will keep in memory (this is used by ZM to make sure it has pre and post images before detecting an alarm - very useful because by the time an alarm is detected, the reason for the alarm may move out of view and a buffer is really useful for this, including for analyzing stats/scores). This value is in the buffers tab for each monitor * target color space is the color depth - 8bit, 24bit or 32bit. It's again in the source tab of each monitor -The 1.2 at the start is basically adding 20% on top of the calculation to account for image/stream overheads (this is an estimate) + +The 20% overhead on top of the calculation to account for image/stream overheads (this is an estimate) The math breakdown for 4 cameras running at 1280x960 capture, 50 frame buffer, 24 bit color space: + :: 1280*960 = 1,228,800 (bytes) @@ -96,8 +101,6 @@ So a good rule of thumb is to make sure you have twice the memory as the calcula As it turns out, ZM uses mapped memory and by default, 50% of your physical memory is what this will grow to. When you reach that limit , ZM breaks down with various errors. -(**Note**: Mapped memory is applicable when you install ZoneMinder with mapped memory support, which is the default mode. If you have specifically disabled mapped memory then please see the next FAQ enty on how to increase shared memory) - A good way to know how much memory is allocated to ZM for its operation is to do a ``df -h`` A sample output on Ubuntu: @@ -116,140 +119,43 @@ For example, if you want to increase this limit to 70% of your memory, add the f where SHMPATH is the ``Mounted on`` path. Here, that would be ``/run/shm``. Other systems may be ``/dev/shm``. -What does a 'Can't shmget: Invalid argument' error in my logs mean? (and my camera does not display at higher resolutions) --------------------------------------------------------------------------------------------------------------------------------------- - -(*Note*: This is applicable for systems that have mapped memory disabled in ZoneMinder. By default, Mapped memory is enabled and unless you have disabled it manually, please refer to the "Math for Memory" question above and how to increase mapped memory limits) - -This error is discussed in the README in the following excerpt:- -''...this is caused by an attempt to allocate an amount of shared memory greater than your system can handle. The size it requests is based on the following formula, ``ring buffer size x image width x image height x 3 (for 24 bit images) + a bit of overhead``. - -So, for example: - -:: - - 384x288 capture resolution, that makes: 110 592 pixels - in 24 bit color that's x 3 = 331,776 bytes per frame - by 80 frames ring buffer x80 = 26,542,080 bytes per camera - by 4 cameras x4 = 106,168,320 bytes. - Plus 10% overhead = 116,785,152 bytes - Thats 114,048 kB, respectively 111.38 MB. - If my shared memory is set to 134,217,728, which is exactly 128MB, - that means I shouldn't have any problem. - (Note that 1kbyte = 1024bytes, 1MB = 1024 kB) - -If for instance you were using 24bit 640x480 then this would come to about 92Mb if you are using the default buffer size of 100. If this is too large then you can either reduce the image or buffer sizes or increase the maximum amount of shared memory available. If you are using RedHat then you can get details on how to change these settings `here `__ - -You should be able to use a similar procedure with other distributions to modify the shared memory pool without kernel recompilations though in some cases this may be necessary. Note, this error also sometimes occurs if you have an old shared memory segment lying around from a previous run that is too small. Use the ipcs and ipcrm system commands to check and remove it if necessary.'" - -You can often find out how many 4KB shared memory pages are available by typing the following :- - -:: - - # cat /proc/sys/kernel/shmall - 2097152 - -In recent kernels the shmall is set to 2097152 memory pages multiplied by 4096 bytes per page for a total of 8 GB of shared memory available. You only need to increase the shmall value if you have a computer with more than 8GB of memory and wish to use more of it for shared memory usage, such as large databases. - -The most shared memory bytes you can allocate in one go :- - -:: - - # cat /proc/sys/kernel/shmmax - 33554432 - -In recent kernels the shmmax is set to 33554432 bytes for only 32 MB of maximum shared memory allocatable at a time, hardly enough for ZoneMinder to go above 320 x 240 x 24-bit resolution at 40 frames in the buffer if it is using the /dev/shm shared memory device, so this value needs to be increased. If you are using ZoneMinder with the memory mapped (mmap) compile time option then this doesn't affect you. - -To change the value to 128 MB temporarily during this kernel execution type (for example) :- -``echo 536870912 >/proc/sys/kernel/shmmax`` - -*Be sure to restart ZoneMinder after this.* - -However be aware that sometimes you will only need to change the shmmax value as shmall is often large enough. Also changing these values in this way is only effective until your machine is rebooted. - -To change them permanently you will need to edit ``/etc/sysctl.conf`` and add the following lines (for example) :- -``kernel.shmmax = 536870912`` - -Or if your distribution has the ``/etc/sysctl.d/`` folder you can create a file in this folder without modifying the ``/etc/sysctl.d`` so you won't lose the changes during distro upgrades :- -```echo kernel.shmmax = 536870912 >/etc/sysctl.d/60-kernel-shm.conf``` - -To load these settings in the sysctl.conf file type: -``sysctl -p`` - -To check your shared memory settings type: -``ipcs -l`` - -Note that with Megapixel cameras like the Axis 207mw becoming cheaper and more attractive, the above memory settings are not adequate. To get Zoneminder working with a full 1280x1024 resolution camera in full color, increase ``134217728`` (128 MB) to, for example, ``268435456`` (256 MB) and multiple this value by each camera. - -These changes will now also be set the next time your machine is restarted. - -Versions 1.24.x of ZoneMinder also allows you to use an alternate method of shared memory allocation, `Mmap mapped memory `__ . This requires less configuration and can be simpler to use. Mapped memory allows you to use a special type of file as the placeholder for your memory and this file is 'mapped' into memory space for easy and fast access. - -To enable mapped memory in ZoneMinder you need add add the --enable--mmap=yes switch to your configure line. By default mapped memory files are created in /dev/shm which on most distributions is a dedicated pseudo-partition containing memory formatted as a filesystem. If your system uses a different path then this can be changed in ZoneMinder in Options->paths->PATH_MAP. It uses a filesystem type called `tmpfs `__. If you type ``df -h`` you should see this area and the size of memory it currently allows. To increase size for tmpfs you need to edit /etc/default/tmpfs. Search for: -``SHM_SIZE=128M`` -and change to something like -``SHM_SIZE=1G`` -then reboot the system. You could possibly need to change RUN_SIZE, too. - -It is important that you do not use a disk based filesystem for your memory mapped files as this will cause memory access to be extremely slow. ZoneMinder creates files called .zm.mmap. in the mapped memory filesystem. - -Mapped memory is subject to the same limitations in terms of total memory as using more traditional shared memory but does not require any configuration per allocation or chunk. In future versions of ZoneMinder this will be the default shared memory storage method. - -Another good article about shared memory settings can be found `here `__ . - -The essential difference was that the kernel.shmall setting is NOT in a direct memory setting in KB but in pages of memory. it is Max Pages of memory - -*For example:* If you want to allocate a maximum memory setting to 8GB you have to convert it to the number of pages (or segments). -with a page size of 4096. -kernel.shmall = 8000x1024x1024/4096 -``kernel.shmall = 2097152`` -NOT 8388608000 as would be suggested in the RedHat article linked above. - -shmmax is the max amount to allocate in one request - -this is is an actual memory size (as opposed to pages) set to 4GB -``kernel.shmmax = 4294967296`` - -The ``/etc/sysctl.conf`` would have these lines - -:: - - kernel.shmall = 2097152 - kernel.shmmax = 4294967296 - -As above, reload your sysctl.conf with ``sysctl -p`` and check that the settings are correct with ``ipcs -l``. I have enabled motion detection but it is not always being triggered when things happen in the camera view --------------------------------------------------------------------------------------------------------------- -ZoneMinder uses zones to examine images for motion detection. When you create the initial zones you can choose from a number of preset values for sensitivity etc. Whilst these are usually a good starting point they are not always suitable for all situations and you will probably need to tweak the values for your specific circumstances. The meanings of the various settings are described in the documentation (`here `__) however if you believe you have sensible settings configured then there are two diagnostic approaches you can use. +ZoneMinder uses zones to examine images for motion detection. When you create the initial zones you can choose from a number of preset values for sensitivity etc. Whilst these are usually a good starting point they are not always suitable for all situations and you will probably need to tweak the values for your specific circumstances. The meanings of the various settings are described in the documentation (`here `__). Another user contributed illustrated Zone definition guide can be found here: `An illustrated guide to Zones `__ + +However if you believe you have sensible settings configured then there are diagnostic approaches you can use. -Another user contributed illustrated Zone definition guide can be found here: `An illustrated guide to Zones `__ Event Statistics ^^^^^^^^^^^^^^^^^ -The first technique is to use event statistics. Firstly you should ensure they are switched on in Options->Logging->RECORD_EVENT_STATS. This will then cause the raw motion detection statistics for any subsequently generated events to be written to the DB. These can then be accessed by first clicking on the Frames or Alarm Frames values of the event from any event list view in the web gui. Then click on the score value to see the actual values that caused the event. Alternatively the stats can be accessed by clicking on the 'Stats' link when viewing any individual frame. The values displayed there correspond with the values that are used in the zone configuration and give you an idea of what 'real world' values are being generated. +The first technique is to use event statistics. Firstly you should ensure they are switched on in ``Options->Logging->RECORD_EVENT_STATS``. This will then cause the raw motion detection statistics for any subsequently generated events to be written to the DB. These can then be accessed by first clicking on the Frames or Alarm Frames values of the event from any event list view in the web gui. Then click on the score value to see the actual values that caused the event. Alternatively the stats can be accessed by clicking on the 'Stats' link when viewing any individual frame. The values displayed there correspond with the values that are used in the zone configuration and give you an idea of what 'real world' values are being generated. Note that if you are investigating why events 'do not' happen then these will not be saved and so won't be accessible. The best thing to do in that circumstance is to make your zone more sensitive so that it captures all events (perhap even ones you don't want) so you can get an idea of what values are being generated and then start to adjust back to less sensitive settings if necessary. You should make sure you test your settings under a variety of lighting conditions (e.g. day and night, sunny or dull) to get the best feel for that works and what doesn't. Using statistics will slow your system down to a small degree and use a little extra disk space in the DB so once you are happy you can switch them off again. However it is perfectly feasible to keep them permanently on if your system is able to cope which will allow you to review your setting periodically. -Diagnostic Images -^^^^^^^^^^^^^^^^^^^^ -The second approach is to use diagnostic images which are saved copies of the intermediate images that ZM uses when determining motion detection. These are switched on and off using Options->Logging->RECORD_DIAG_IMAGES. +Diagnostic Images along with FIFO +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The second approach is to use diagnostic images which are saved copies of the intermediate images that ZM uses when determining motion detection. These are switched on and off using ``Options->Logging->RECORD_DIAG_IMAGES``. + +.. note:: In addition to the detailed explanation below, a recently added ``RECORD_DIAG_IMAGES_FIFO`` option, also available in ``Options->Logging`` can be an invaluable tool to see how your current motion settings are affecting motion detection. The ``delta`` stream along with the ``raw`` (json output) stream can be invaluable to see the effect in real time. Please refer to the explanation of this feature in :doc:`/userguide/options/options_logging` There are two kinds of diagnostic images which are and are written (and continuously overwritten) to the top level monitor event directory. If an event occurs then the files are additionally copied to the event directory and renamed with the appropriate frame number as a prefix. -The first set are produced by the monitor on the image as a whole. The diag-r.jpg image is the current reference image against which all individual frames are compared and the diag-d.jpg image is the delta image highlighting the difference between the reference image and the last analysed image. In this images identical pixels will be black and the more different a pixel is the whiter it will be. Viewing this image and determining the colour of the pixels is a good way of getting a feel for the pixel differences you might expect (often more than you think). +The first set are produced by the monitor on the image as a whole. The ``diag-r.jpg`` image is the current reference image against which all individual frames are compared and the ``diag-d.jpg`` image is the delta image highlighting the difference between the reference image and the last analysed image. In this images identical pixels will be black and the more different a pixel is the whiter it will be. Viewing this image and determining the colour of the pixels is a good way of getting a feel for the pixel differences you might expect (often more than you think). -The second set of diag images are labelled as diag--.jpg where zoneid is the id of the zone in question (Smile) and the stage is where in the alarm check process the image is generated from. So if you have several zones you can expect to see multiple files. Also these files are only interested in what is happening in their zone only and will ignore anything else outside of the zone. The stages that each number represents are as follows, +The second set of diag images are labelled as ``diag--.jpg`` where zoneid is the id of the zone in question (Smile) and the stage is where in the alarm check process the image is generated from. So if you have several zones you can expect to see multiple files. Also these files are only interested in what is happening in their zone only and will ignore anything else outside of the zone. The stages that each number represents are as follows, -# Alarmed Pixels - This image shows all pixels in the zone that are considered to be alarmed as white pixels and all other pixels as black. -# Filtered Pixels - This is as stage one except that all pixels removed by the filters are now black. The white pixels represent the pixels that are candidates to generate an event. -# Raw Blobs - This image contains all alarmed pixels from stage 2 but aggrageted into blobs. Each blob will have a different greyscale value (between 1 and 254) so they can be difficult to spot with the naked eye but using a colour picker or photoshop will make it easier to see what blob is what. -# Filtered Blobs - This image is as stage 3 but under (or over) sized blobs have been removed. This is the final step before determining if an event has occurred, just prior to the number of blobs being counted. Thus this image forms the basis for determining whether an event is generated and outlining on alarmed images is done from the blobs in this image. +* Alarmed Pixels - This image shows all pixels in the zone that are considered to be alarmed as white pixels and all other pixels as black. +* Filtered Pixels - This is as stage one except that all pixels removed by the filters are now black. The white pixels represent the pixels that are candidates to generate an event. +* Raw Blobs - This image contains all alarmed pixels from stage 2 but aggrageted into blobs. Each blob will have a different greyscale value (between 1 and 254) so they can be difficult to spot with the naked eye but using a colour picker or photoshop will make it easier to see what blob is what. +* Filtered Blobs - This image is as stage 3 but under (or over) sized blobs have been removed. This is the final step before determining if an event has occurred, just prior to the number of blobs being counted. Thus this image forms the basis for determining whether an event is generated and outlining on alarmed images is done from the blobs in this image. Using the above images you should be able to tell at all stages what ZM is doing to determine if an event should happen or not. They are useful diagnostic tools but as is mentioned elsewhere they will massively slow your system down and take up a great deal more space. You should never leave ZM running for any length of time with diagnostic images on. + Why can't ZoneMinder capture images (either at all or just particularly fast) when I can see my camera just fine in xawtv or similar? ---------------------------------------------------------------------------------------------------------------------------------------------- @@ -259,14 +165,14 @@ On average a card that can capture at 25fps per chip PAL for one input will do m When using xawtv etc to view the stream you are not looking at an image captured using the frame grabber but the card's video memory mapped onto your screen. This requires no capture or processing unless you do an explicit capture via the J or ctrl-J keys for instance. Some cards or drivers do not support the frame grabber interface at all so may not work with ZoneMinder even though you can view the stream in xawtv. If you can grab a still using the grab functionality of xawtv then in general your card will work with ZoneMinder. -Why can't I see streamed images when I can see stills in the Zone window etc? +Why can't I see streamed images when I can see stills in the zone window etc? ------------------------------------------------------------------------------------- This issue is normally down to one of two causes -1) You are using Internet Explorer and are trying to view multi-part jpeg streams. IE does not support these streams directly, unlike most other browsers. You will need to install Cambozola or another multi-part jpeg aware plugin to view them. To do this you will need to obtain the applet from the Downloads page and install the cambozola.jar file in the same directory as the ZoneMinder php files. Then find the ZoneMinder Options->Images page and enable ZM_OPT_CAMBOZOLA and enter the web path to the .jar file in ZM_PATH_CAMBOZOLA. This will ordinarily just be cambozola.jar. Provided (Options / B/W tabs) WEB_H_CAN_STREAM is set to auto and WEB_H_STREAM_METHOD is set to jpeg then Cambozola should be loaded next time you try and view a stream. +1) You are using Internet Explorer and are trying to view multi-part jpeg streams. IE does not support these streams directly, unlike most other browsers. You will need to install Cambozola or another multi-part jpeg aware plugin to view them. To do this you will need to obtain the applet from the Downloads page and install the cambozola.jar file in the same directory as the ZoneMinder php files. Then find the ZoneMinder Options->Images page and enable ``OPT_CAMBOZOLA`` and enter the web path to the .jar file in ``PATH_CAMBOZOLA``. This will ordinarily just be cambozola.jar. Provided (Options / B/W tabs) ``WEB_H_CAN_STREAM`` is set to auto and ``WEB_H_STREAM_METHOD`` is set to jpeg then Cambozola should be loaded next time you try and view a stream. -'''NOTE''': If you find that the Cambozola applet loads in IE but the applet just displays the version # of Cambozola and the author's name (as opposed to seeing the streaming images), you may need to chmod (''-rwxrwxr-x'') your (''usr/share/zoneminder/'') cambozola.jar: +**NOTE**: If you find that the Cambozola applet loads in IE but the applet just displays the version of Cambozola and the author's name (as opposed to seeing the streaming images), you may need to chmod (``-rwxrwxr-x``) your (``usr/share/zoneminder/``) cambozola.jar: :: @@ -274,9 +180,7 @@ This issue is normally down to one of two causes Once I did this, images started to stream for me. -2) The other common cause for being unable to view streams is that you have installed the ZoneMinder cgi binaries (zms and nph-zms) in a different directory than your web server is expecting. Make sure that the --with-cgidir option you use to the ZoneMinder configure script is the same as the CGI directory configure for your web server. If you are using Apache, which is the most common one, then in your httpd.conf file there should be a line like ``ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"`` where the last directory in the quotes is the one you have specified. If not then change one or the other to match. Be warned that configuring apache can be complex so changing the one passed to the ZoneMinder configure (and then rebuilding and reinstalling) is recommended in the first instance. If you change the apache config you will need to restart apache for the changes to take effect. If you still cannot see stream reliably then try changing Options->Paths->ZM_PATH_ZMS to just use zms if nph-zms is specified, or vice versa. Also check in your apache error logs. - -Also, please check the value of the ZM_PATH_ZMS setting under the Paths Options tab. It is where you configure the URL to the zms or nph-zms CGI executable. Under most Debian-based distros this value should be /zm/cgi-bin/nph-zms but in the past may have been /cgi-bin/nph-zms or you may have configured it to be something else. +2) The other common cause for being unable to view streams is that you have installed the ZoneMinder cgi binaries (zms and nph-zms) in a different directory than your web server is expecting. Make sure that the --with-cgidir option you use to the ZoneMinder configure script is the same as the CGI directory configure for your web server. If you are using Apache, which is the most common one, then in your httpd.conf file there should be a line like ``ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"`` where the last directory in the quotes is the one you have specified. If not then change one or the other to match. Be warned that configuring apache can be complex so changing the one passed to the ZoneMinder configure (and then rebuilding and reinstalling) is recommended in the first instance. If you change the apache config you will need to restart apache for the changes to take effect. If you still cannot see stream reliably then try changing ``ZM_PATH_ZMS`` in your ``/etc/zm/config`` directory to just use zms if nph-zms is specified, or vice versa. Also check in your apache error logs. Lastly, please look for errors created by the zmc processes. If zmc isn't running, then zms will not be able to get an image from it and will exit. @@ -302,6 +206,11 @@ change the 3 to a 1 network.http.max-persistent-connections-per-proxy -> 100 again network.http.max-persistent-connections-per-server -> 100 again + +I can't see more than 6 monitors in montage on my browser +--------------------------------------------------------- +Browsers such a Chrome and Safari only support upto 6 streams from the same domain. To work around that, take a look at the multi-port configuration discussed in the ``MIN_STREAMING_PORT`` configuration in :doc:`/userguide/options/options_network` + Why is ZoneMinder using so much CPU? --------------------------------------- @@ -315,8 +224,8 @@ The main causes are. * Big image sizes. A image of 640x480 requires at least four times the processing of a 320x240 image. Experiment with different sizes to see what effect it may have. Sometimes a large image is just two interlaced smaller frames so has no real benefit anyway. This is especially true for analog cameras/cards as image height over 320 (NTSC) or 352 PAL) are invariably interlaced. * Capture frame rates. Unless there's a compelling reason in your case there is often little benefit in running cameras at 25fps when 5-10fps would often get you results just as good. Try changing your monitor settings to limit your cameras to lower frame rates. You can still configure ZM to ignore these limits and capture as fast as possible when motion is detected. * Run function. Obviously running in Record or Mocord modes or in Modect with lots of events generates a lot of DB and file activity and so CPU and load will increase. - * Basic default detection zones. By default when a camera is added one detection zone is added which covers the whole image with a default set of parameters. If your camera covers a view in which various regions are unlikely to generate a valid alarm (ie the sky) then I would experiment with reducing the zone sizes or adding inactive zones to blank out areas you don't want to monitor. Additionally the actual settings of the zone themselves may not be optimal. When doing motion detection the number of changed pixels above a threshold is examined, then this is filter, then contiguous regions are calculated to see if an alarm is generated. If any maximum or minimum threshold is exceeded according to your zone settings at any time the calculation stops. If your settings always result in the calculations going through to the last stage before being failed then additional CPU time is used unnecessarily. Make sure your maximum and minimumzone thresholds are set to sensible values and experiment by switching RECORD_EVENT_STATS on and seeing what the actual values of alarmed pixels etc are during sample events. - * Optimise your settings. After you've got some settings you're happy with then switching off RECORD_EVENT_STATS will prevent the statistics being written to the database which saves some time. Other settings which might make a difference are ZM_FAST_RGB_DIFFS and the JPEG_xxx_QUALITY ones. + * Basic default detection zones. By default when a camera is added one detection zone is added which covers the whole image with a default set of parameters. If your camera covers a view in which various regions are unlikely to generate a valid alarm (ie the sky) then I would experiment with reducing the zone sizes or adding inactive zones to blank out areas you don't want to monitor. Additionally the actual settings of the zone themselves may not be optimal. When doing motion detection the number of changed pixels above a threshold is examined, then this is filter, then contiguous regions are calculated to see if an alarm is generated. If any maximum or minimum threshold is exceeded according to your zone settings at any time the calculation stops. If your settings always result in the calculations going through to the last stage before being failed then additional CPU time is used unnecessarily. Make sure your maximum and minimumzone thresholds are set to sensible values and experiment by switching ``RECORD_EVENT_STATS`` on and seeing what the actual values of alarmed pixels etc are during sample events. + * Optimise your settings. After you've got some settings you're happy with then switching off ``RECORD_EVENT_STATS`` will prevent the statistics being written to the database which saves some time. Other settings which might make a difference are ``ZM_FAST_RGB_DIFFS`` and the ``JPEG_xxx_QUALITY`` ones. I'm sure there are other things which might make a difference such as what else you have running on the box and memory sizes (make sure there's no swapping going on). Also speed of disk etc will make some difference during event capture and also if you are watching the whole time then you may have a bunch of zms processes running also. @@ -330,17 +239,19 @@ Using the timeline view is only recommended when using FireFox, however even the This function has from time to time been corrupted in the SVN release or in the stable releases, try and reinstall from a fresh download. +.. _disk_bw_faq: + How much Hard Disk Space / Bandwidth do I need for ZM? --------------------------------------------------------------- -Please see `this excel sheet `__ or `this online excel sheet `__ (both are user contributed excel sheets) +Please see `this online excel sheet `__. Note that this is just an estimate -Or go to `this link `__ for the Axis bandwidth calculator. Although this is aimed at Axis cameras it still produces valid results for any kind of IP camera. +Or go to `this link `__ for the Axis bandwidth calculator. Although this is aimed at Axis cameras it still produces valid results for any kind of IP camera. As a quick guide I have 4 cameras at 320x240 storing 1 fps except during alarm events. After 1 week 60GB of space in the volume where the events are stored (/var/www/html/zm) has been used. When I try and run ZoneMinder I get lots of audit permission errors in the logs and it won't start ------------------------------------------------------------------------------------------------------- -Many Linux distributions nowadays are built with security in mind. One of the latest methods of achieving this is via SELinux (Secure Linux) which controls who is able to run what in a more precise way then traditional accounting and file based permissions (`link `__). +Many Linux distributions nowadays are built with security in mind. One of the latest methods of achieving this is via SELinux (Secure Linux) which controls who is able to run what in a more precise way then traditional accounting and file based permissions (`link `__). If you are seeing entries in your system log like: Jun 11 20:44:02 kernel: audit(1150033442.443:226): avc: denied { read } for pid=5068 @@ -353,38 +264,13 @@ Note that SELinux may cause errors other than those listed above. If you are in How do I enable ZoneMinder's security? ------------------------------------------- -In the console, click on Options. Check the box next to "ZM_OPT_USE_AUTH". You will immediately be asked to login. The default username is 'admin' and the password is 'admin'. +In the console, click on ``Options->System``. Check the box next to ``ZM_OPT_USE_AUTH``. You will immediately be asked to login. The default username is 'admin' and the password is 'admin'. To Manage Users: -In main console, go to Options->Users. +In main console, go to ``Options->Users``. -You may also consider to use the web server security, for example, htaccess files under Apache scope; You may even use this as an additional/redundant security on top of Zoneminders built-in security features; +You may also consider to use the web server security, for example, htaccess files under Apache scope; You may even use this as an additional/redundant security on top of Zoneminders built-in security features. Note that if you choose to enable webserver auth, zmNinja may have issues. Please read the `zmNinja FAQ on basic authentication `__ for more information. Also please note that zmNinja does not support digest authentication. -Why does ZM stop recording once I have 32000 events for my monitor? ------------------------------------------------------------------------- -Storing more than 32k files in a single folder is a limitation of some filesystems. To avoid this, enable USE_DEEP_STORAGE under Options. - -USE_DEEP_STORAGE is now the default for new ZoneMinder systems so this limitation should only apply to users upgrading from a previous version of ZoneMinder. - -Versions of ZM from 1.23.0 onwards allow you to have a deeper filesystem with fewer files per individual directory. As well as not being susceptible to the 32k limit, this is also somewhat faster. - -If you have upgraded from a previous version of ZoneMinder and this option is not already enabled, it is very important to follow the steps below to enable it on an existing system. Failure to properly follow these steps **WILL RESULT IN LOSS OF YOUR DATA!** - -:: - - # Stop ZoneMinder - # Backup your event data and the dB if you have the available storage - # Enable USE_DEEP_STORAGE under Options. - # From the command line, run "sudo zmupdate.pl --migrate-events" - # Monitor the output for any events that fail to convert. - # After the conversion completes, you can restart ZoneMinder - -Note that you can re-run the migrate-events command if any error messages scroll off the screen. - -You can read about the lack of a limit in the number of sub-directories in the ext4 filesystem at: `this link `__ -and see what tools may assist in your use of this filesystem `here `__ -If you search for ext3 or reiserfs on the forums you will find various threads on this issue with guidance on -how to convert. Managing system load (with IP Cameras in mind) ---------------------------------------------------- @@ -410,7 +296,7 @@ Zoneminder runs on Linux, Linux measures system load using "load", which is comp A load of 1.0 means the processor has "just enough to do right now". Also worth noting that a load of 4.0 means exactly the same for a quad processor machine - each number equals a single processor's workload. A very high load can be fine on a computer that has a stacked workload - such as a machine sending out bulk emails, or working its way through a knotty problem; it'll just keep churning away until it's done. However - Zoneminder needs to process information in real time so it can't afford to stack its jobs, it needs to deal with them right away. -For a better and full explanation of Load: `Please read this `__ +For a better and full explanation of Load: `Please read this `__ My load is too high, how can I reduce it? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -427,7 +313,7 @@ Zoneminder is *very* tweakable and it's possible to tune it to compromise. The f * Experiment with using jpeg instead of mjpeg. Some users have reported it gives better performance, but YMMV. - * Tweak the zones. Keep them as small and as few as possible. Stick to one zone unless you really need more. Read `this `__ for an easy to understand explanation along with the official Zone guide. + * Tweak the zones. Keep them as small and as few as possible. Stick to one zone unless you really need more. Read `this `__ for an easy to understand explanation along with the official Zone guide. * Schedule. If you are running a linux system at near capacity, you'll need to think carefully about things like backups and scheduled tasks. updatedb - the process which maintains a file database so that 'locate' works quickly, is normally scheduled to run once a day and if on a busy system can create a heavy increase on the load. The same is true for scheduled backups, especially those which compress the files. Re-schedule these tasks to a time when the cpu is less likely to be busy, if possible - and also use the "nice" command to reduce their priority. (crontab and /etc/cron.daily/ are good places to start) @@ -440,73 +326,16 @@ More expensive options: * Faster CPU. Simple but effective. Zoneminder also works very well with multiple processor systems out of the box (if SMP is enabled in your kernel). The load of different cameras is spread across the processors. - * Try building Zoneminder with processor specific instructions that are optimised to the system it will be running on, also increasing the optimisation level of GCC beyond -O2 will help. + * Try building Zoneminder with processor specific instructions that are optimised to the system it will be running on, also increasing the optimisation level of GCC beyond -O2 will help. This topic is beyond the scope of this document. -:: - - ./configure CFLAGS="-g -O3 -march=athlon-xp -mtune=athlon-xp" CXXFLAGS="-g -O3 -march=athlon-xp -mtune=athlon-xp" - -The above command is optimised for an Athlon XP cpu so you will need to use the specific processor tag for your cpu, also the compiler optimisation has been increased to -O3. - -You also need to put in your normal ./configure commands as if you were compiling with out this optimisation. - -A further note is that the compile must be performed on the system that Zoneminder will be running on as this optimisation will make it hardware specific code. - -Processor specific commands can be found in the GCC manual along with some more options that may increase performanc. -``__ - -The below command has been used to compile Zoneminder on a Athlon XP system running CentOS 5.5 and along with the libjpeg-turbo modification to reduce the CPU load in half, libjpeg-turbo reduced the load by 1/3 before the processor optimisation. -:: - - ./configure --with-webdir=/var/www/html/zm --with-cgidir=/var/www/cgi-bin CFLAGS="-g -O3 -march=athlon-xp -mtune=athlon-xp" CXXFLAGS="-D__STDC_CONSTANT_MACROS -g -O3 -march=athlon-xp -mtune=athlon-xp" --enable-mmap --sysconfdir=/etc/zm - -The following command has been used to compile Zoneminder 1.25 on a CentOS 6.0 system, the native command should choose the processor automatically during compile time, this needs to be performed on the actual system!!. - -:: - - CFLAGS="-g -O3 -march=native -mtune=native" CXXFLAGS="-D__STDC_CONSTANT_MACROS -g -O3 -march=native -mtune=native" ./configure --with-webdir=/var/www/html/zm --with-cgidir=/var/www/cgi-bin --with-webuser=apache --with-webgroup=apache ZM_DB_HOST=localhost ZM_DB_NAME=zm ZM_DB_USER=your_zm_user ZM_DB_PASS=your_zm_password ZM_SSL_LIB=openssl +Processor specific commands can be found in the `GCC manual `__ along with some more options that may increase performance. What about disks and bandwidth? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A typical 100mbit LAN will cope with most setups easily. If you're feeding from cameras over smaller or internet links, obviously fps will be much lower. -Disk and Bandwidth calculators are referenced on the Zoneminder wiki here: http://www.zoneminder.com/wiki/index.php/FAQ#How_much_Hard_Disk_Space_.2F_Bandwidth_do_I_need_for_ZM.3F - - -Building ZoneMinder --------------------- - -When running configure I am getting a lot of messages about not being able to compile the ffmpeg libraries -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If you see output from configure that looks like this - -:: - - checking libavcodec/avcodec.h usability... no - checking libavcodec/avcodec.h presence... yes - configure: WARNING: libavcodec/avcodec.h: present but cannot be compiled - configure: WARNING: libavcodec/avcodec.h: check for missing - prerequisite headers? - configure: WARNING: libavcodec/avcodec.h: see the Autoconf documentation - configure: WARNING: libavcodec/avcodec.h: section "Present But - Cannot Be Compiled" - configure: WARNING: libavcodec/avcodec.h: proceeding with the compiler's - result - configure: WARNING: ## ------------------------------------- ## - configure: WARNING: ## Report this to support@zoneminder.com ## - configure: WARNING: ## ------------------------------------- ## - -then it is caused not by the ZoneMinder build system but ffmpeg itself. However there is a workaround you can use which is to add ``CPPFLAGS=-D__STDC_CONSTANT_MACROS`` - -to the ZoneMinder ``./configure`` command which should solve the issue. However this is not a proper 'fix' as such, which can only come from the ffmpeg project itself. - -I cannot build ZoneMinder and am getting lots of undefined C++ template errors -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - -This is almost certainly due to the 'ccache' package which attempts to speed up compilation by caching compiled objects. Unfortunately one of the side effects is that it breaks the GNU g++ template resolution method that ZoneMinder uses in building by prevent files getting recompiled. The simplest way around this is to remove the ccache package using your distros package manager. +Disk and Bandwidth calculators are referenced in :ref:`disk_bw_faq`. How do I build for X10 support? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -530,7 +359,7 @@ To save a run state you should first configure your monitors for Modect, Record, Now you can switch between these two states by selecting them from the same dialog you saved them, or from the command line from issue the command ''zmpkg.pl '', for example ''zmpkg.pl Daytime''. -The final step you need to take, is scheduling the time the changes take effect. For this you can use `cron `__. A simple entry to change to the Daylight state at at 8am and to the nighttime state at 8pm would be as follows, +The final step you need to take, is scheduling the time the changes take effect. For this you can use `cron `__. A simple entry to change to the Daylight state at at 8am and to the nighttime state at 8pm would be as follows, :: @@ -544,50 +373,10 @@ Although the example above describes changing states at different times of day, How can I use ZoneMinder to trigger something else when there is an alarm? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -ZoneMinder includes a perl API which means you can create a script to interact with the ZM shared memory data and use it in your own scripts to react to ZM alarms or to trigger ZM to generate new alarms. Full details are in the README or by doing ``perldoc ZoneMinder``, ``perldoc ZoneMinder::SharedMem`` etc. -Below is an example script that checks all monitors for alarms and when one occurs, prints a message to the screen. You can add in your own code to make this reaction a little more useful. +ZoneMinder includes a perl API which means you can create a script to interact with the ZM shared memory data and use it in your own scripts to react to ZM alarms or to trigger ZM to generate new alarms. Full details are in the README or by doing ``perldoc ZoneMinder`` etc. -:: +ZoneMinder provides a sample alarm script called `zmalarm.pl `__ that you can refer to as a starting point. - #!/usr/bin/perl -w - - use strict; - - use ZoneMinder; - - $| = 1; - - zmDbgInit( "myscript", level=>0, to_log=>0, to_syslog=>0, to_term=>1 ); - - my $dbh = DBI->connect( "DBI:mysql:database=".ZM_DB_NAME.";host=".ZM_DB_HOST, ZM_DB_USER, ZM_DB_PASS ); - - my $sql = "select M.*, max(E.Id) as LastEventId from Monitors as M left join Events as E on M.Id = E.MonitorId where M.Function != 'None' group by (M.Id)"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - - my $res = $sth->execute() or die( "Can't execute '$sql': ".$sth->errstr() ); - my @monitors; - while ( my $monitor = $sth->fetchrow_hashref() ) - { - push( @monitors, $monitor ); - } - - while( 1 ) - { - foreach my $monitor ( @monitors ) - { - next if ( !zmMemVerify( $monitor ) ); - - if ( my $last_event_id = zmHasAlarmed( $monitor, $monitor->{LastEventId} ) ) - { - $monitor->{LastEventId} = $last_event_id; - print( "Monitor ".$monitor->{Name}." has alarmed\n" ); - # - # Do your stuff here - # - } - } - sleep( 1 ); - } Trouble Shooting ------------------- @@ -596,13 +385,9 @@ This is also how to obtain the info that we need to help you on the forums. What logs should I check for errors? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -ZoneMinder creates its own logs and are usually located in the ``/tmp`` directory. +ZoneMinder creates its own logs and are usually located in the ``/var/log/`` directory. Refer to the logging discussion in :doc:`/userguide/options/options_logging` for more details on where logs are stored and how to enable various log levels. -The ZoneMinder logs for the RPM packages are located in ``/var/log/zm``. - -Depending on your problem errors can show up in any of these logs but, usually the logs of interest are ``zmdc.log`` and ``zmpkg.log`` if ZM is not able to start. - -Now since ZM is dependent on other components to work, you might not find errors in ZM but in the other components. +Since ZM is dependent on other components to work, you might not find errors in ZM but in the other components. :: @@ -618,8 +403,6 @@ If ZM is not functioning, you should always be able to find an error in at least This will append any data entered to any of these logs to your console screen (``-f``). To exit, hit [ctrl -c]. -More verbose logging for the ZoneMinder binaries is available by enabling the debug option from the control panel and will be placed in the path you have configured for the debug logs. Output can be limited to a specific binary as described in the Debug options page under the "?" marks. - How can I trouble shoot the hardware and/or software? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -643,29 +426,33 @@ The apache web server needs to have the right permissions and configuration to b Why am I getting broken images when trying to view events? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Zoneminder and the Apache web server need to have the right permissions. Check this forum topic and similar ones: -http://www.zoneminder.com/forums/viewtopic.php?p=48754#48754 +Zoneminder and the Apache web server need to have the right permissions. Check `this forum topic `__ and similar ones: + I can review events for the current day, but ones from yesterday and beyond error out ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If you've checked that the `www-data` user has permissions to the storage folders, perhaps your php.ini's timezone setting is incorrect. They _must_ match for certain playback functions. +If you've checked that the ``www-data`` user has permissions to the storage folders, perhaps your php.ini's timezone setting is incorrect. They _must_ match for certain playback functions. -If you're using Linux, this can be found using the following command: :: +If you're using Linux, this can be found using the following command: + +:: timedatectl | grep "Time zone" -If using FreeBSD, you can use this one-liner: :: +If using FreeBSD, you can use this one-liner: + +:: cd /usr/share/zoneinfo/ && find * -type f -exec cmp -s {} /etc/localtime \; -print; -Once you know what timezone your system is set to, open `/etc/php.ini` and adjust ``date.timezone`` to the appropriate value. the PHP daemon may need to be restarted for changes to take effect. +Once you know what timezone your system is set to make sure you set the right time zone in ZM (Available in ``Options->System->TimeZone``) Why is the image from my color camera appearing in black and white? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If you recently upgraded to zoneminder 1.26, there is a per camera option that defaults to black and white and can be mis-set if your upgrade didn't happen right. See this thread: http://www.zoneminder.com/forums/viewtopic.php?f=30&t=21344 +If you recently upgraded to zoneminder 1.26, there is a per camera option that defaults to black and white and can be mis-set if your upgrade didn't happen right. See this thread: https://forums.zoneminder.com/viewtopic.php?f=30&t=21344 This may occur if you have a NTSC analog camera but have configured the source in ZoneMinder as PAL for the Device Format under the source tab. You may also be mislead because zmu can report the video port as being PAL when the camera is actually NTSC. Confirm the format of your analog camera by checking it's technical specifications, possibly found with the packaging it came in, on the manufacturers website, or even on the retail website where you purchased the camera. Change the Device Format setting to NTSC and set it to the lowest resolution of 320 x 240. If you have confirmed that the camera itself is NTSC format, but don't get a picture using the NTSC setting, consider increasing the shared memory '''kernel.shmall''' and '''kernel.shmmax''' settings in /etc/sysctl.conf to a larger value such as 268435456. This is also the reason you should start with the 320x240 resolution, so as to minimize the potential of memory problems which would interfere with your attempts to troubleshoot the device format issue. Once you have obtained a picture in the monitor using the NTSC format, then you can experiment with raising the resolution. @@ -675,39 +462,17 @@ If this camera is attached to a capture card, then you may have selected the wro Why do I only see black screens with a timestamp when monitoring my camera? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -In the monitor windows where you see the black screen with a timestamp, select settings and enter the Brightness, Contrast, Hue, and Color settings reported for the device by '''zmu -d -q -v'''. 32768 may be appropriate values to try for these settings. After saving the settings, select Settings again to confirm they saved successfully. +In the monitor windows where you see the black screen with a timestamp, select settings and enter the Brightness, Contrast, Hue, and Color settings reported for the device by ``zmu -d -q -v``. 32768 may be appropriate values to try for these settings. After saving the settings, select Settings again to confirm they saved successfully. -I am getting messages about a backtrace in my logs, what do I do? -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If you are seeing entries in your log like the following - -:: - - Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /lib64/libc.so.6 [0x3347230210]] - Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /lib64/libc.so.6(memset+0xce) [0x334727684e]] - Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /usr/local/bin/zma [0x40ee9a]] - Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /usr/local/bin/zma [0x419946]] - Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /usr/local/bin/zma [0x4213cf]] - Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /usr/local/bin/zma(cos+0x35c) [0x404674]] - Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /lib64/libc.so.6(__libc_start_main+0xf4) [0x334721da44]] - Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /usr/local/bin/zma(cos+0xd1) [0x4043e9]] - Jan 11 20:25:22 localhost zma_m2[19051]: INF [Backtrace complete] - -then you can help diagnose the problem by running a special command to translate the hex addresses into helpful information. This command is called addr2line and you can type 'man addr2line' for more information. -Basically addr2line takes two sets of parameters, the first is the name of the binary file, and the second is a list of addresses. Both of these pieces of information are displayed in the logs. The filename is the first part after the 'Backtrace:' tag, in this case /usr/local/bin/zma, though it may well be different in your case. Some of the lines refer to libraries rather than the zma executable but those can be ignored for now, the important part is noting which ZM binary is involved. The binary file is passed in following the -e flag. The addresses to pass to addr2line are those contained in the '[]' pairs. Again you can ignore those that are on a line that refers to a library but it will not hurt if you include them. -So in the example above, the command would be ``addr2line -e /usr/local/bin/zma 0x40ee9a 0x419946 0x4213cf 0x404674 0x4043e9`` -This should then dump out a more symbolic list containing source file names and line numbers, and it is this information which will be helpful if posted to the forums. Sometimes addr2line fails to produce useful output. This is usually because either the problem is so severe that it has corrupted the stack and prevented useful information from being displayed, or that you have either compiled ZM without the -g flag for debug, or you have stripped the binaries of symbol information after installation. This this case you would need to rebuild temporarily with debug enabled for the information to be useful. - - -This error some times happens when a linked camera looses its link or it is corrupted by the user or some other system event, try deleting the affected cameras and recreating them in the Zoneminder console. How do I repair the MySQL Database? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ There is two ways to go about this. In most cases you can run from the command prompt -> -* mysqlcheck --all-databases --auto-repair -p'''your_database_password''' -u '''your_databse_user''' +``mysqlcheck --all-databases --auto-repair -p your_database_password -u your_databse_user`` -If that does not work then you will have to make sure that ZoneMinder is stopped then run the following (nothing should be using the database while running this and you will have to adjust for your correct path if it is different). -> -* myisamchk --silent --force --fast --update-state -O key_buffer=64M -O sort_buffer=64M -O read_buffer=1M -O write_buffer=1M /var/lib/mysql/*/*.MYI +If that does not work then you will have to make sure that ZoneMinder is stopped then run the following (nothing should be using the database while running this and you will have to adjust for your correct path if it is different): + +``myisamchk --silent --force --fast --update-state -O key_buffer=64M -O sort_buffer=64M -O read_buffer=1M -O write_buffer=1M /var/lib/mysql/*/*.MYI`` How do I repair the MySQL Database when the cli fails? @@ -715,7 +480,7 @@ How do I repair the MySQL Database when the cli fails? In Ubuntu, the commands listed above do not seem to work. However, actually doing it by hand from within MySQL does. (But that is beyond the scope of this document) But that got me thinking... And phpmyadmin does work. Bring up a terminal. ``sudo apt-get install phpmyadmin`` -Now go to http://zoneminder_IP/ and stop the ZM service. Continue to http://zoneminder_IP/phpmyadmin and select the zoneminder database. Select and tables marked 'in use' and pick the action 'repare' to fix. Restart the zoneminder service from the web browser. Remove or disable the phpmyadmin tool, as it is not always the most secure thing around, and opens your database wide to any skilled hacker. +Now go to ``http://zoneminder_IP/`` and stop the ZM service. Continue to ``http://zoneminder_IP/phpmyadmin`` and select the zoneminder database. Select and tables marked 'in use' and pick the action 'repare' to fix. Restart the zoneminder service from the web browser. Remove or disable the phpmyadmin tool, as it is not always the most secure thing around, and opens your database wide to any skilled hacker. ``sudo apt-get remove phpmyadmin`` I upgraded by distribution and ZM stopped working @@ -760,10 +525,13 @@ The ZoneMinder license is described at the end of the documentation and consists 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. -This means that ZoneMinder is licensed under the terms described `here `__. There is a comprehensive FAQ covering the GPL at http://www.gnu.org/licenses/gpl-faq.html but in essence you are allowed to redistribute or modify GPL licensed software provided that you release your distribution or modifications freely under the same terms. You are allowed to sell systems based on GPL software. You are not allowed to restrict or reduce the rights of GPL software in your distribution however. Of course if you are just making modifications for your system locally you are not releasing changes so you have no obligations in this case. I recommend reading the GPL FAQ for more in-depth coverage of this issue. +This means that ZoneMinder is licensed under the terms described `here `__. There is a comprehensive FAQ covering the GPL at https://www.gnu.org/licenses/gpl-faq.html but in essence you are allowed to redistribute or modify GPL licensed software provided that you release your distribution or modifications freely under the same terms. You are allowed to sell systems based on GPL software. You are not allowed to restrict or reduce the rights of GPL software in your distribution however. Of course if you are just making modifications for your system locally you are not releasing changes so you have no obligations in this case. I recommend reading the GPL FAQ for more in-depth coverage of this issue. Can I use ZoneMinder as part of my commercial product? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The GPL license allows you produce systems based on GPL software provided your systems also adhere to that license and any modifications you make are also released under the same terms. The GPL does not permit you to include ZoneMinder in proprietary systems (see http://www.gnu.org/licenses/gpl-faq.html#GPLInProprietarySystem for details). If you wish to include ZoneMinder in this kind of system then you will need to license ZoneMinder under different terms. This is sometimes possible and you will need to contact me for further details in these circumstances. +The GPL license allows you produce systems based on GPL software provided your systems also adhere to that license and any modifications you make are also released under the same terms. The GPL does not permit you to include ZoneMinder in proprietary systems (see https://www.gnu.org/licenses/gpl-faq.html#GPLInProprietarySystem for details). If you wish to include ZoneMinder in this kind of system then you will need to license ZoneMinder under different terms. This is sometimes possible and you will need to contact me for further details in these circumstances. +I am having issues with zmNinja and/or Event Notification Server +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +zmNinja and the Event Notification Server are 3rd party solutions. The developer maintains exhaustive `documentation and FAQs `__. Please direct your questions there. \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 46da979cf..740e9ec26 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,7 +10,9 @@ ZoneMinder Documentation faq contributing -Welcome to ZoneMinder's documentation, the following resources are available +Welcome to ZoneMinder's documentation. Please navigate to one of the links below. + +If you are facing issues that are not covered in the documentation, please feel free to check the `ZoneMinder Forums `__ or join the `ZoneMinder-Chat Slack channel `__ if you prefer real time interaction. :doc:`installationguide/index` Many distribution repos only hold older versions of ZoneMinder, current versions contain many bug fixes and updated functionality. Instructions here for installing updated packages or compiling from source. @@ -27,6 +29,9 @@ Welcome to ZoneMinder's documentation, the following resources are available :doc:`contributing` How to contribute to ZoneMinder. As a community project we always need help, you don't need to be a coder to test or update documentation. +`Event Notification Server and Machine Learning hooks `__ + Documentation for the 3rd party Event Notification Server and Machine Learning for Object/People/Face detection. + Indices and tables diff --git a/docs/installationguide/debian.rst b/docs/installationguide/debian.rst index 74a9ca81d..d09423bd9 100644 --- a/docs/installationguide/debian.rst +++ b/docs/installationguide/debian.rst @@ -3,10 +3,161 @@ Debian .. contents:: +Easy Way: Debian Buster +------------------------ + +This procedure will guide you through the installation of ZoneMinder on Debian 10 (Buster). + +**Step 1:** Make sure your system is up to date + +Open a console and use ``su`` command to become root. + +:: + + apt update + apt upgrade + + +**Step 2:** Setup Sudo (optional but recommended) + +By default Debian does not come with sudo, so you have to install it and configure it manually. +This step is optional but recommended and the following instructions assume that you have setup sudo. +If you prefer to setup ZoneMinder as root, do it at your own risk and adapt the following instructions accordingly. + +:: + + apt install sudo + usermod -a -G sudo + exit + +Now your terminal session is back under your normal user. You can check that +you are now part of the sudo group with the command ``groups``, "sudo" should +appear in the list. If not, run ``newgrp sudo`` and check again with ``groups``. + + +**Step 3:** Install Apache and MySQL + +These are not dependencies for the ZoneMinder package as they could be +installed elsewhere. If they are not installed yet in your system, you have to +trigger their installation manually. + +:: + + sudo apt install apache2 default-mysql-server + +**Step 4:** Add ZoneMinder's Package repository to your apt sources + +ZoneMinder's Debian packages are not included in Debian's official package +repositories. To be able to install ZoneMinder with APT, you have to edit the +list of apt sources and add ZoneMinder's repository. + +Add the following to the /etc/apt/sources.list.d/zoneminder.list file + +:: + + # ZoneMinder repository + deb https://zmrepo.zoneminder.com/debian/release-1.34 buster/ + +You can do this using: + +:: + echo "deb https://zmrepo.zoneminder.com/debian/release-1.34 buster/" | sudo tee /etc/apt/sources.list.d/zoneminder.list + +Because ZoneMinder's package repository provides a secure connection through HTTPS, apt must be enabled for HTTPS. +:: + + sudo apt install apt-transport-https + +Ensure you have gnupg installed before importing the apt key in the following step. +:: + + sudo apt install gnupg + + +Finally, download the GPG key for ZoneMinder's repository: +:: + + wget -O - https://zmrepo.zoneminder.com/debian/archive-keyring.gpg | sudo apt-key add - + + +**Step 5:** Install ZoneMinder + +:: + + sudo apt update + sudo apt install zoneminder + +**Step 6:** Read the Readme + +The rest of the install process is covered in the README.Debian, so feel free to have +a read. + +:: + + zcat /usr/share/doc/zoneminder/README.Debian.gz + + +**Step 7:** Enable ZoneMinder service + +:: + + sudo systemctl enable zoneminder.service + +**Step 8:** Configure Apache + +The following commands will setup the default /zm virtual directory and configure +required apache modules. + +:: + + sudo a2enconf zoneminder + sudo a2enmod rewrite # this is enabled by default + sudo a2enmod cgi # this is done automatically when installing the package. Redo this command manually only for troubleshooting. + + +**Step 9:** Edit Timezone in PHP + +Automated way: +:: + + sudo sed -i "s/;date.timezone =/date.timezone = $(sed 's/\//\\\//' /etc/timezone)/g" /etc/php/7.*/apache2/php.ini + +Manual way +:: + + sudo nano /etc/php/7.*/apache2/php.ini + +Search for [Date] (Ctrl + w then type Date and press Enter) and change +date.timezone for your time zone. Don't forget to remove the ; from in front +of date.timezone. + +:: + + [Date] + ; Defines the default timezone used by the date functions + ; http://php.net/date.timezone + date.timezone = America/New_York + +CTRL+o then [Enter] to save + +CTRL+x to exit + + +**Step 10:** Start ZoneMinder + +Reload Apache to enable your changes and then start ZoneMinder. + +:: + + sudo systemctl reload apache2 + sudo systemctl start zoneminder + +You are now ready to go with ZoneMinder. Open a browser and type either ``localhost/zm`` one the local machine or ``{IP-OF-ZM-SERVER}/zm`` if you connect from a remote computer. + Easy Way: Debian Stretch ------------------------ -This procedure will guide you through the installation of ZoneMinder on Debian 9 (Stretch). This section has been tested with ZoneMinder 1.32.3 on Debian 9.8. +This procedure will guide you through the installation of ZoneMinder on Debian 9 (Stretch). This section has been tested with ZoneMinder 1.34 on Debian 9.8. **Step 1:** Make sure your system is up to date @@ -52,7 +203,7 @@ Add the following to the bottom of the file :: # ZoneMinder repository - deb https://zmrepo.zoneminder.com/debian/release stretch/ + deb https://zmrepo.zoneminder.com/debian/release-1.34 stretch/ CTRL+o and to save CTRL+x to exit @@ -275,7 +426,6 @@ CTRL+x to exit **Step 12:** Please check the configuration -Zoneminder 1.32.x 1. Check path of ZM_PATH in '/etc/zm/conf.d/zmcustom.conf' is ZM_PATH_ZMS=/zm/cgi-bin/nph-zms :: cat /etc/zm/conf.d/zmcustom.conf @@ -309,8 +459,8 @@ Reload Apache to enable your changes and then start ZoneMinder. :: { - "version": "1.29.0", - "apiversion": "1.29.0.1" + "version": "1.34.0", + "apiversion": "1.34.0.1" } **Congratulations** Your installation is complete diff --git a/docs/installationguide/easydocker.rst b/docs/installationguide/easydocker.rst new file mode 100644 index 000000000..899ce0e5d --- /dev/null +++ b/docs/installationguide/easydocker.rst @@ -0,0 +1,11 @@ +An Easy To Use Docker Image +=========================== +If you are interested in trying out ZoneMinder quickly, user Dan Landon maintains an easy to use docker image for ZoneMinder. With a few simple configuration changes, it also provides complete Event Notification Server and Machine Learning hook support. Please follow instructions in his repostory. He maintains two repositories: + +* If you want to run the latest stable release, please use his `zoneminder repository `__. +* If you want to run the latest zoneminder master, please use his `zoneminder master repository `__. + +In both cases, instructions are provided in the repo README files. + +If you are looking at building your own native (non docker) binary packages of ZoneMinder for your distro, please refer to the distro specific install guides or :doc:`packpack`. + diff --git a/docs/installationguide/images/zm_first_screen_post_install.png b/docs/installationguide/images/zm_first_screen_post_install.png index 568d7fcf5..59e2af6d3 100644 Binary files a/docs/installationguide/images/zm_first_screen_post_install.png and b/docs/installationguide/images/zm_first_screen_post_install.png differ diff --git a/docs/installationguide/index.rst b/docs/installationguide/index.rst index 4e2288de6..602632c94 100644 --- a/docs/installationguide/index.rst +++ b/docs/installationguide/index.rst @@ -1,14 +1,18 @@ Installation Guide ====================================== +.. todo:: This entire guide needs to be checked/updated as needed + Contents: .. toctree:: :maxdepth: 2 + easydocker packpack ubuntu debian redhat + windows_wsl multiserver dedicateddrive diff --git a/docs/installationguide/packpack.rst b/docs/installationguide/packpack.rst index dd54ef4b2..72fdf8661 100644 --- a/docs/installationguide/packpack.rst +++ b/docs/installationguide/packpack.rst @@ -1,6 +1,8 @@ All Distros - A Docker Way to Build ZoneMinder =============================================== +.. note:: If you are looking for an easy way to run ZoneMinder and not interested in building your own docker image, please refer to :doc:`easydocker`. + .. contents:: These instructions represent an alternative way to build ZoneMinder for any supported distro. @@ -124,4 +126,4 @@ More testing needs to be done for Redhat distros but it appears Fedora users can sudo systemctl start systemd-binfmt -TO-DO: Verify the details behind enabling qemu emulation on redhat distros. Pull requests are welcome. +.. todo:: Verify the details behind enabling qemu emulation on redhat distros. Pull requests are welcome. diff --git a/docs/installationguide/redhat.rst b/docs/installationguide/redhat.rst index 5b5067058..c18a3412a 100644 --- a/docs/installationguide/redhat.rst +++ b/docs/installationguide/redhat.rst @@ -53,7 +53,7 @@ ZoneMinder releases are now being hosted at RPM Fusion. New users should navigat Note that RHEL/CentOS 7 users should use yum instead of dnf. -Once ZoneMinder has been installed, it is critically important that you read the README file under /usr/share/doc/zoneminder. ZoneMinder will not run without completing the steps outlined in the README. +Once ZoneMinder has been installed, it is critically important that you read the README file under /usr/share/doc/zoneminder-common. ZoneMinder will not run without completing the steps outlined in the README. How to Install Nightly Development Builds ----------------------------------------- @@ -101,7 +101,7 @@ Certain commands in these instructions require root privileges while other comma Set Up Your Environment *********************** -Before you begin, set up an rpmbuild environment by following `this guide `_ by the CentOS developers. +Before you begin, set up an rpmbuild environment by following `this guide `_ by the CentOS developers. In addition, make sure RPM Fusion is enabled as described in the previous section `How to Install ZoneMinder`_. @@ -191,7 +191,7 @@ Now clone the ZoneMinder git repository from your home folder: git clone https://github.com/ZoneMinder/zoneminder cd zoneminder -This will create a sub-folder called ZoneMinder, which will contain the latest development source code. +This will create a sub-folder called zoneminder, which will contain the latest development source code. If you have previsouly cloned the ZoneMinder git repo and wish to update it to the most recent, then issue these commands instead: diff --git a/docs/installationguide/ubuntu.rst b/docs/installationguide/ubuntu.rst index 582b8e5c5..7b714e51a 100644 --- a/docs/installationguide/ubuntu.rst +++ b/docs/installationguide/ubuntu.rst @@ -3,8 +3,8 @@ Ubuntu .. contents:: -Easy Way: Ubuntu 18.04 ----------------------- +Easy Way: Ubuntu 18.04 (Bionic) +------------------------------- These instructions are for a brand new ubuntu 18.04 system which does not have ZM installed. @@ -15,6 +15,7 @@ achieve the same result by running: :: + sudo apt-get install tasksel sudo tasksel install lamp-server During installation it will ask you to set up a master/root password for the MySQL. @@ -41,16 +42,7 @@ guide you with a quick search. :: - add-apt-repository ppa:iconnor/zoneminder - - Please note that as of 1.32.0 We are creating a new PPA for each major version, as a means to prevent automatic upgrades from one major version to another. So instead of the above ppa line use the following: - - :: - - add-apt-repository ppa:iconnor/zoneminder-1.32 - - If you are on Trusty or Xenial, you may want to add both, as there are some packages for dependencies included in the old ppa. - + add-apt-repository ppa:iconnor/zoneminder-1.34 Update repo and upgrade. @@ -163,7 +155,7 @@ You may also want to enable to following modules to improve caching performance nano /etc/php/7.2/apache2/php.ini Search for [Date] (Ctrl + w then type Date and press Enter) and change -date.timezone for your time zone, see [this](http://php.net/manual/en/timezones.php). +date.timezone for your time zone, see [this](https://www.php.net/manual/en/timezones.php). **Don't forget to remove the ; from in front of date.timezone** :: @@ -202,8 +194,8 @@ CTRL+x to exit PPA install may need some tweaking of ZMS_PATH in ZoneMinder options. `Socket_sendto or no live streaming`_ -Easy Way: Ubuntu 16.04 ----------------------- +Easy Way: Ubuntu 16.04 (Xenial) +------------------------------- These instructions are for a brand new ubuntu 16.04 system which does not have ZM installed. @@ -241,16 +233,8 @@ guide you with a quick search. :: add-apt-repository ppa:iconnor/zoneminder - - Please note that as of 1.32.0 We are creating a new PPA for each major version, as a means to prevent automatic upgrades from one major version to another. So instead of the above ppa line use the following: - - :: - add-apt-repository ppa:iconnor/zoneminder-1.32 - If you are on Trusty or Xenial, you may want to add both, as there are some packages for dependencies included in the old ppa. - - Update repo and upgrade. :: @@ -362,7 +346,7 @@ You may also want to enable to following modules to improve caching performance nano /etc/php/7.0/apache2/php.ini Search for [Date] (Ctrl + w then type Date and press Enter) and change -date.timezone for your time zone, see [this](http://php.net/manual/en/timezones.php). +date.timezone for your time zone, see [this](https://www.php.net/manual/en/timezones.php). **Don't forget to remove the ; from in front of date.timezone** :: @@ -401,8 +385,8 @@ CTRL+x to exit PPA install may need some tweaking of ZMS_PATH in ZoneMinder options. `Socket_sendto or no live streaming`_ -Easy Way: Ubuntu 14.x ---------------------- +Easy Way: Ubuntu 14.x (Trusty) +------------------------------ **These instructions are for a brand new ubuntu 14.x system which does not have ZM installed.** **Step 1:** Either run commands in this install using sudo or use the below to become root @@ -450,7 +434,7 @@ Easy Way: Ubuntu 14.x nano /etc/php5/apache2/php.ini Search for [Date] (Ctrl + w then type Date and press Enter) and change -date.timezone for your time zone, see [this](http://php.net/manual/en/timezones.php). +date.timezone for your time zone, see [this](https://www.php.net/manual/en/timezones.php). **Don't forget to remove the ; from in front of date.timezone** :: diff --git a/docs/installationguide/windows_wsl.rst b/docs/installationguide/windows_wsl.rst new file mode 100644 index 000000000..6861f522e --- /dev/null +++ b/docs/installationguide/windows_wsl.rst @@ -0,0 +1,10 @@ +Windows 10+ using WSL +======================= + +With Windows 10, Microsoft released the `Window Subsystem for Linux (WSL) `__ that enables you to run native Linux tools directly on Windows, alongside your traditional Windows desktop and modern store apps. To install WSL, please refer to the +`installation guide from Microsoft `__. + +ZoneMinder now runs on Windows 10+ systems which have WSL enabled. This guide will explain how to go about installing an Ubuntu ZM image on Windows 10+. + +.. todo:: Isaac to add instructions. + diff --git a/docs/userguide/components.rst b/docs/userguide/components.rst index 48aff0be4..064e27d5f 100644 --- a/docs/userguide/components.rst +++ b/docs/userguide/components.rst @@ -17,8 +17,6 @@ Binaries This is the ZoneMinder Capture daemon. This binary's job is to sit on a video device and suck frames off it as fast as possible, this should run at more or less constant speed. **zma** This is the ZoneMinder Analysis daemon. This is the component that goes through the captured frames and checks them for motion which might generate an alarm or event. It generally keeps up with the Capture daemon but if very busy may skip some frames to prevent it falling behind. -**zmf** - This is the ZoneMinder Frame daemon. This is an optional daemon that can run in concert with the Analysis daemon and whose function it is to actually write captured frames to disk. This frees up the Analysis daemon to do more analysis (!) and so keep up with the Capture daemon better. If it isn’t running or dies then the Analysis daemon just writes them itself. **zms** This is the ZoneMinder Streaming server. The web interface connects with this to get real-time or historical streamed images. It runs only when a live monitor stream or event stream is actually being viewed and dies when the event finishes or the associate web page is closed. If you find you have several zms processes running when nothing is being viewed then it is likely you need a patch for apache (see the Troubleshooting section). A non-parsed header version of zms, called nph-zms, is also installed and may be used instead depending on your web server configuration. **zmu** @@ -41,11 +39,11 @@ Finally some perl scripts in the scripts directory. These scripts all have some **zmpkg.pl** This is the ZoneMinder Package Control script. This is used by the web interface and service scripts to control the execution of the system as a whole. **zmdc.pl** - This is the ZoneMinder Daemon Control script. This is used by the web interface and the zmpkg.pl script to control and maintain the execution of the capture and analysis daemons, amongst others. You should not need to run this script yourself. + This is the ZoneMinder Daemon Control script. This is used by the web interface and the zmpkg.pl script to control and maintain the execution of the capture and analysis daemons, amongst others. You should not need to run this script yourself, although you can use it to start/top individual ZM processes. **zmfilter.pl** - This script controls the execution of saved filters and will be started and stopped by the web interface based on whether there are filters that have been defined to be autonomous. This script is also responsible for the automatic uploading of events to a 3rd party server. + This script controls the execution of saved filters and will be started and stopped by the web interface based on whether there are filters that have been defined to be autonomous(background). This script is also responsible for the automatic uploading of events to a 3rd party server. Prior to 1.32 there was one zmfilter.pl process. In 1.32 onwards we start a zmfilter.pl process for each background filter so that the processing time of one filter doesn't delay the processing of another filter. **zmaudit.pl** - This script is used to check the consistency of the event file system and database. It can delete orphaned events, i.e. ones that appear in one location and not the other as well as checking that all the various event related tables are in line. It can be run interactively or in batch mode either from the command line or a cron job or similar. In the zmconfig.pl there is an option to specify fast event deletes where the web interface only deletes the event entry from the database itself. If this is set then it is this script that tidies up the rest. + This script is used to check the consistency of the event file system and database. It can delete orphaned events, i.e. ones that appear in one location and not the other as well as checking that all the various event related tables are in line. It can be run interactively or in batch mode either from the command line or a cron job or similar. In the zmconfig.pl there is an option to specify fast event deletes where the web interface only deletes the event entry from the database itself. If this is set then it is this script that tidies up the rest. We do not recommend fast event deletion and we do not recommend having zmaudit.pl run in the background. It is a very ram cpu and disk io intensive program, constantly scanning every event. Please run it manually or from a cron job on weekends or something. **zmwatch.pl** This is a simple script purely designed to keep an eye on the capture daemons and restart them if they lockup. It has been known for sync problems in the video drivers to cause this so this script makes sure that nothing important gets missed. **zmupdate.pl** @@ -74,6 +72,9 @@ Finally some perl scripts in the scripts directory. These scripts all have some **zm** This is the (optional) ZoneMinder init script, see below for details. +**zmeventnotification.pl** + This is an optional 3rd party real time event notification server that also provides push notifications for zmNinja as well as machine learning powered object/face-detection. Please see `Event Notification Server Documentation `__ for more details (Note that the machine learning components are optional, and are developed in Python3) + Finally, there are also a number of ZoneMinder perl modules included. These are used by the scripts above, but can also be used by your own or 3rd party scripts. Full documentation for most modules is available in ‘pod’ form via ‘perldoc’ but the general purpose of each module is as follows. **ZoneMinder.pm** @@ -86,9 +87,15 @@ Finally, there are also a number of ZoneMinder perl modules included. These are This module contains the defined Debug and Error functions etc, that are used by scripts to produce diagnostic information in a standard format. **ZoneMinder/Database.pm** This module contains database access definitions and functions. Currently not a lot is in this module but it is included as a placeholder for future development. +**ZoneMinder/Event.pm** + This module contains functions to load, manipulate, delete, copy, move events. +**ZoneMinder/Filter.pm** + This module contains functions to load, execute etc filters. **ZoneMinder/SharedMem.pm** This module contains standard shared memory access functions. These can be used to access the current state of monitors etc as well as issuing commands to the monitors to switch things on and off. This module effectively provides a ZoneMinder API. **ZoneMinder/ConfigAdmin.pm** This module is a specialised module that contains the definition, and other information, about the various configuration options. It is not intended for use by 3rd parties. +**ZoneMinder/Control/\*.pm** + These modules contain implementations of the various PTZ protocols. **ZoneMinder/Trigger/\*.pm** These modules contain definitions of trigger channels and connections used by the zmtrigger.pl script. Although they can be used ‘as is’, they are really intended as examples that can be customised or specialised for different interfaces. Contributed modules for new channels or connections will be welcomed and included in future versions of ZoneMinder. diff --git a/docs/userguide/configfiles.rst b/docs/userguide/configfiles.rst new file mode 100644 index 000000000..879d181fd --- /dev/null +++ b/docs/userguide/configfiles.rst @@ -0,0 +1,47 @@ +Configuration Files +-------------------- +This section describes configuration files that ZoneMinder uses beyond the various Web UI options. + +.. _replacement_for_options_path: + +System Path Configurations +~~~~~~~~~~~~~~~~~~~~~~~~~~ +At one point of time, ZoneMinder stored various system path configurations under the Web UI (``Options->Paths``). This was removed a few versions ago and now resides in a configuration file. The motivation for this change can be read in `this discussion `__. + +Typically, path configurations now reside in ``/etc/zm``. + +Here is an example of the file hierarchy: + +:: + + /etc/zm + ├── conf.d + │   ├── 01-system-paths.conf + │   ├── 02-multiserver.conf + | ├── 03-custom.conf #optional + │   └── README + ├── objectconfig.ini # optional + ├── zm.conf + └── zmeventnotification.ini #optional + +The roles of the files are as follows: + +* ``zm.conf`` contains various base configuration entries. You should not edit this file as it may be overwritten on an upgrade. +* ``zmeventnotification.ini`` is only present if you have installed the ZoneMinder Event Notification Server. +* ``objectconfig.ini`` is only present if you have installed the machine learning hooks for the Event Notification Server. +* ``conf.d`` contains additional configuration items as follows: + + * ``01-system-paths.conf`` contains all the paths that were once part of ``Options->Paths`` in the Web UI. You should not edit this file as it may be overwritten on an upgrade + * ``02-multiserver.conf`` file consists of custom variables if you are deploying ZoneMinder in a multi-server configuration (see :doc:`/installationguide/multiserver`) + * ``03-custom.conf`` is an custom config file that I created to override specific variables in the path files. **This is the recommended way to customize entries**. Anything that you want to change should be in a new file inside ``conf.d``. Note that ZoneMinder will sort all the files alphabetically and run their contents in ascending order. So it doesn't really matter what you name them, as long as you make sure your changes are not overwritten by another file in the sorting sequence. It is therefore good practice to prefix your file names by ``nn-`` where ``nn`` is a monotonically increasing numerical sequence ``01-`` ``02-`` ``03-`` and so forth, so you know the order they will be processed. + +Timezone Configuration +~~~~~~~~~~~~~~~~~~~~~~~ +Earlier versions of ZoneMinder relied on ``php.ini`` to set Date/Time Zone. This is no longer the case. You can (and must) set the Timezone via the Web UI, starting ZoneMinder version 1.34. See :ref:`here `. + +Database Specific Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. todo:: do we really need to have this section? Not sure if its generic and not specific to ZM + +While the ZoneMinder specific database config entries reside in ``/etc/zm/zm.conf`` and related customizations discussed above, general database configuration items can be tweaked in ``/etc/mysql`` (or whichever path your DB server is installed) \ No newline at end of file diff --git a/docs/userguide/definemonitor.rst b/docs/userguide/definemonitor.rst index 7641cb751..a5b622872 100644 --- a/docs/userguide/definemonitor.rst +++ b/docs/userguide/definemonitor.rst @@ -46,6 +46,10 @@ Linked Monitors This field allows you to select other monitors on your system that act as triggers for this monitor. So if you have a camera covering one aspect of your property you can force all cameras to record while that camera detects motion or other events. You can either directly enter a comma separated list of monitor ids or click on ‘Select’ to choose a selection. Be very careful not to create circular dependencies with this feature however you will have infinitely persisting alarms which is almost certainly not what you want! To unlink monitors you can ctrl-click. Maximum FPS + + .. warning:: + Unless you know what you are doing, please leave this field empty, especially if you are configuring a network camera. More often than not, putting a value here adversely affects recording. + On some occasions you may have one or more cameras capable of high capture rates but find that you generally do not require this performance at all times and would prefer to lighten the load on your server. This option permits you to limit the maximum capture rate to a specified value. This may allow you to have more cameras supported on your system by reducing the CPU load or to allocate video bandwidth unevenly between cameras sharing the same video device. This value is only a rough guide and the lower the value you set the less close the actual FPS may approach it especially on shared devices where it can be difficult to synchronise two or more different capture rates precisely. This option controls the maximum FPS in the circumstance where no alarm is occurring only. This feature is limited and will only work under the following conditions: @@ -56,6 +60,11 @@ Maximum FPS Using this field for video streams from IP cameras will cause undesirable results when the value is equal to or less than the frame rate from the camera. Note that placing a value higher than the camera's frame rate is allowed and can help prevent cpu spikes when communication from the camera is lost. Alarm Maximum FPS + + .. warning:: + Unless you know what you are doing, please leave this field empty, especially if you are configuring a network camera. More often than not, putting a value here adversely affects recording. + + If you have specified a Maximum FPS it may be that you don’t want this limitation to apply when your monitor is recording motion or other event. This setting allows you to override the Maximum FPS value if this circumstance occurs. As with the Maximum FPS setting leaving this blank implies no limit so if you have set a maximum fps in the previous option then when an alarm occurs this limit would be ignored and ZoneMinder would capture as fast as possible for the duration of the alarm, returning to the limited value after the alarm has concluded. Equally you could set this to the same, or higher (or even lower) value than Maximum FPS for more precise control over the capture rate in the event of an alarm. **IMPORTANT:** This field is subject to the same limitations as the Maximum FPS field. Ignoring these limitations will produce undesriable results. @@ -71,15 +80,15 @@ Source Tab FFmpeg ^^^^^^ - This is the recommended source type for most modern ip cameras. + This is the **recommended** source type for most modern ip cameras. Source Path Use this field to enter the full URL of the stream or file your camera supports. This is usually an RTSP url. There are several methods to learn this: * Check the documentation that came with your camera - * Look for your camera in the hardware compatibilty list in the wiki http://wiki.zoneminder.com/Hardware_Compatibility_List + * Look for your camera in the hardware compatibilty list in the `hardware compatibility wiki `__ * Try ZoneMinder's new ONVIF probe feature - * Download and install the ONVIF Device Manager onto a Windows machine https://sourceforge.net/projects/onvifdm/ + * Download and install the `ONVIF Device Manager `__ onto a Windows machine * Use Google to find third party sites, such as ispy, which document this information Source Colours Specify the amount of colours in the captured image. 32 bit is the preferred choice here. Unlike with local cameras changing this has no controlling effect on the remote camera itself so ensure that your camera is actually capturing to this palette beforehand. @@ -121,7 +130,7 @@ Remote Protocol Remote Method When HTTP is the Remote Protocol, your choices are Simple and Regexp. Most should choose Simple. When RTSP is the Remote Protocol, your choices are RTP/Unicast, RTP/Multicast, RTP/RTSP, RTP,RTSP,HTTP. Try each of these to determine which works with your camera. Most cameras will use either RTP/Unicast (UDP) or RTP/RTSP (TCP). Remote Host/Port/Path - Use these fields to enter the full URL of the camera. Basically if your camera is at http://camserver.home.net:8192/cameras/camera1.jpg then these fields will be camserver.home.net, 8192 and /cameras/camera1.jpg respectively. Leave the port at 80 if there is no special port required. If you require authentication to access your camera then add this onto the host name in the form :@.com. This will usually be 32 or 24 bit colour even if the image looks black and white. Look in Supported Hardware > Network Cameras section, how to obtain these strings that may apply to your camera. + Use these fields to enter the full URL of the camera. Basically if your camera is at ``http://camserver.home.net:8192/cameras/camera1.jpg`` then these fields will be camserver.home.net, 8192 and /cameras/camera1.jpg respectively. Leave the port at 80 if there is no special port required. If you require authentication to access your camera then add this onto the host name in the form :@.com. This will usually be 32 or 24 bit colour even if the image looks black and white. Look in Supported Hardware > Network Cameras section, how to obtain these strings that may apply to your camera. Remote Image Colours Specify the amount of colours in the captured image. Unlike with local cameras changing this has no controlling effect on the remote camera itself so ensure that your camera is actually capturing to this palette beforehand. Capture Width/Height @@ -271,3 +280,7 @@ Default Scale If your monitor has been defined with a particularly large or small image size then you can choose a default scale here with which to view the monitor so it is easier or more visible from the web interface. Web Colour Some elements of ZoneMinder now use colours to identify monitors on certain views. You can select which colour is used for each monitor here. Any specification that is valid for HTML colours is valid here, e.g. ‘red’ or ‘#ff0000’. A small swatch next to the input box displays the colour you have chosen. +Embed EXIF data into image: + Embeds EXIF data into each jpeg frame + + .. todo:: what about mp4s? diff --git a/docs/userguide/definezone.rst b/docs/userguide/definezone.rst index 97f42f864..1d00c71b4 100644 --- a/docs/userguide/definezone.rst +++ b/docs/userguide/definezone.rst @@ -40,7 +40,7 @@ Type Preset The preset chooser sets sensible default values based on computational needs (fast v. best) and sensitivity (low, medium, high.) It is not required that you select a preset, and you can alter any of the parameters after choosing a preset. For a small number of monitors with ZoneMinder running on modern equipment, Best, high sensitivity can be chosen as a good starting point. - It is important to understand that the available presets are intended merely as a starting point. Since every camera's view is unique, they are not guaranteed to work properly in every case. Presets tend to work acceptably for indoor cameras, where the objects of interest are relatively close and there typically are few or no unwanted objects moving within the cameras view. Presets, on the other hand, tend to not work acceptably for outdoor cameras, where the field of view is typically much wider, objects of interest are farther away, and changing weather patterns can cause false triggers. For outdoor cameras in particular, you will almost certainly have to tune your motion detection zone to get desired results. Please refer to `this guide `__ to learn how to do this. + It is important to understand that the available presets are intended merely as a starting point. Since every camera's view is unique, they are not guaranteed to work properly in every case. Presets tend to work acceptably for indoor cameras, where the objects of interest are relatively close and there typically are few or no unwanted objects moving within the cameras view. Presets, on the other hand, tend to not work acceptably for outdoor cameras, where the field of view is typically much wider, objects of interest are farther away, and changing weather patterns can cause false triggers. For outdoor cameras in particular, you will almost certainly have to tune your motion detection zone to get desired results. Please refer to `this guide `__ to learn how to do this. Units * Pixels - Selecting this option will allow many of the following values to be entered (or viewed) in units of pixels. @@ -109,5 +109,5 @@ Extend Alarm Frame Count Other information ----------------- -Refer to `this `__ user contributed Zone guide for additional information will illustrations if you are new to zones and need more help. +Refer to `this `__ user contributed Zone guide for additional information will illustrations if you are new to zones and need more help. diff --git a/docs/userguide/filterevents.rst b/docs/userguide/filterevents.rst index d3c4955c9..436034da0 100644 --- a/docs/userguide/filterevents.rst +++ b/docs/userguide/filterevents.rst @@ -8,10 +8,7 @@ Filters allow you to define complex conditions with associated actions in ZoneMi And many more. -The filter window can be accessed from various views, one of which is to simply tap on the filter button in the main web view: - -.. image:: images/filter-button.png - :width: 600px +The filter window can be accessed by tapping on the top level filter menu You can use the filter window to create your own filters or to modify existing ones. You can even save your favourite filters to re-use at a future date. Filtering itself is fairly simple; you first choose how many expressions you'd like your filter to contain. Changing this value will cause the window to redraw with a corresponding row for each expression. You then select what you want to filter on and how the expressions relate by choosing whether they are 'and' or 'or' relationships. For filters comprised of many expressions you will also get the option to bracket parts of the filter to ensure you can express it as desired. Then if you like choose how you want your results sorted and whether you want to limit the amount of events displayed. @@ -22,62 +19,78 @@ Here is what the filter window looks like :width: 800px * *A*: This is a dropdown list where you can select pre-defined filters. You will notice that ZoneMinder comes with a PurgeWhenFull filter that is configured to delete events if you reach 95% of disk space. -* *B* and *C*: This is where you specify conditions that need to match before the filter is executed. You use the "+" and "-" buttons to add/delete conditions -* *D*: This is where you specify what needs to happen when the conditions match: +* *B*: If you are creating a new filter, you can type in a name for your filter here +* *C*: This is where you specify conditions that need to match before the filter is executed. You use the "+" and "-" buttons to add/delete conditions +* *D*: This allows you to perform sorting and limiting operations on the output before you take an action +* *E*: This is where you specify what needs to happen when the conditions match: + * Archive all matches: sets the archive field to 1 in the Database for the matched events. Think of 'archiving' as grouping them under a special category - you can view archived events later and also make sure archived events don't get deleted, for example - * Email details of all matches: Sends an email to the configured address with details about the event. - The email can be customized as per TBD - * Execute command on all matches: Allows you to execute any arbitrary command on the matched events. You can use replacement tokens as subsequent arguents to the command, the last argument will be the absolute path to the event, preceeded by replacement arguents. eg: /usr/bin/script.sh %MN% will excecute as /usr/bin/script.sh MonitorName /path/to/event. - * Delete all matches: Deletes all the matched events -* *E*: Use 'Submit' to 'test' your matching conditions. This will just match and show you what filters match. Use 'Execute' to actually execute the action after matching your conditions. Use 'Save' to save the filter for future use and 'Reset' to clear your settings + + .. todo :: + fill in what update used disk space, copy all matches, move all matches do. For the "create video" filter, put in more details on how it works, any dependencies etc. -.. NOTE:: More details on filter conditions: + * Update used disk space: + * Create video for all matches: creates a video file of all the events that match + * Execute command on all matches: Allows you to execute any arbitrary command on the matched events. You can use replacement tokens as subsequent arguents to the command, the last argument will be the absolute path to the event, preceeded by replacement arguents. eg: /usr/bin/script.sh %MN% will excecute as /usr/bin/script.sh MonitorName /path/to/event. + * Delete all matches: Deletes all the matched events. + * Copy all matches: + * Move all matches: + * Run filter in background: When checked, ZoneMinder will make sure the filter is checked regularly. For example, if you want to be notified of new events by email, you should make sure this is checked. Filters that are configured to run in the background have a “*” next to it. + * Run filter concurrently: Allows this filter to run in its own thread thereby letting other filters run in parallel. + +* *F*: Use 'List Matches' to 'test' your matching conditions. This will just match and show you what filters match. Use 'Execute' to actually execute the action after matching your conditions. Use 'Save' to save the filter for future use and 'Reset' to clear your settings + +.. note:: More details on filter conditions: There are several different elements to an event that you can filter on, some of which require further explanation. These are as follows, * 'Date/Time' which must evaluate to a date and a time together, * 'Date' and 'Time' which are variants which may only contain the relevant subsets of this, * 'Weekday' which as expected is a day of the week. - All of the preceding elements take a very flexible free format of dates and time based on the PHP strtotime function (http://www.php.net/manual/en/function.strtotime.php). This allows values such as 'last Wednesday' etc to be entered. We recommend acquainting yourself with this function to see what the allowed formats are. However automated filters are run in perl and so are parsed by the Date::Manip package. Not all date formats are available in both so if you are saved your filter to do automatic deletions or other tasks you should make sure that the date and time format you use is compatible with both methods. The safest type of format to use is ‘-3 day’ or similar with easily parseable numbers and units are in English. + All of the preceding elements take a very flexible free format of dates and time based on the PHP strtotime function (https://www.php.net/manual/en/function.strtotime.php). This allows values such as 'last Wednesday' etc to be entered. We recommend acquainting yourself with this function to see what the allowed formats are. However automated filters are run in perl and so are parsed by the Date::Manip package. Not all date formats are available in both so if you are saved your filter to do automatic deletions or other tasks you should make sure that the date and time format you use is compatible with both methods. The safest type of format to use is ‘-3 day’ or similar with easily parseable numbers and units are in English. The other things you can filter on are all fairly self explanatory, except perhaps for 'Archived' which you can use to include or exclude Archived events. In general you'll probably do most filtering on un-archived events. There are also two elements, Disk Blocks and Disk Percent which don’t directly relate to the events themselves but to the disk partition on which the events are stored. These allow you to specify an amount of disk usage either in blocks or in percentage as returned by the ‘df’ command. They relate to the amount of disk space used and not the amount left free. Once your filter is specified, clicking 'submit' will filter the events according to your specification. As the disk based elements are not event related directly if you create a filter and include the term ‘DiskPercent > 95’ then if your current disk usage is over that amount when you submit the filter then all events will be listed whereas if it is less then none at all will. As such the disk related terms will tend to be used mostly for automatic filters (see below). If you have created a filter you want to keep, you can name it and save it by clicking 'Save'. If you do this then the subsequent dialog will also allow you specify whether you want this filter automatically applied in order to delete events or upload events via ftp to another server and mail notifications of events to one or more email accounts. Emails and messages (essentially small emails intended for mobile phones or pagers) have a format defined in the Options screen, and may include a variety of tokens that can be substituted for various details of the event that caused them. This includes links to the event view or the filter as well as the option of attaching images or videos to the email itself. Be aware that tokens that represent links may require you to log in to access the actual page, and sometimes may function differently when viewed outside of the general ZoneMinder context. The tokens you can use are as follows. - * %EI% Id of the event - * %EN% Name of the event - * %EC% Cause of the event - * %ED% Event description - * %ET% Time of the event - * %EL% Length of the event - * %EF% Number of frames in the event - * %EFA% Number of alarm frames in the event - * %EST% Total score of the event - * %ESA% Average score of the event - * %ESM% Maximum score of the event - * %EP% Path to the event - * %EPS% Path to the event stream - * %EPI% Path to the event images - * %EPI1% Path to the first alarmed event image - * %EPIM% Path to the (first) event image with the highest score - * %EI1% Attach first alarmed event image - * %EIM% Attach (first) event image with the highest score - * %EV% Attach event mpeg video - * %MN% Name of the monitor - * %MET% Total number of events for the monitor - * %MEH% Number of events for the monitor in the last hour - * %MED% Number of events for the monitor in the last day - * %MEW% Number of events for the monitor in the last week - * %MEM% Number of events for the monitor in the last month - * %MEA% Number of archived events for the monitor - * %MP% Path to the monitor window - * %MPS% Path to the monitor stream - * %MPI% Path to the monitor recent image - * %FN% Name of the current filter that matched - * %FP% Path to the current filter that matched - * %ZP% Path to your ZoneMinder console + * %EI% Id of the event + * %EN% Name of the event + * %EC% Cause of the event + * %ED% Event description + * %ET% Time of the event + * %EL% Length of the event + * %EF% Number of frames in the event + * %EFA% Number of alarm frames in the event + * %EST% Total score of the event + * %ESA% Average score of the event + * %ESM% Maximum score of the event + * %EP% Path to the event + * %EPS% Path to the event stream + * %EPF1% Path to the frame view for the first alarmed event image + * %EPFM% Path to the frame view for the (first) event image with the highest score + * %EFMOD% Path to image containing object detection, in frame view + * %EPI% Path to the event images + * %EPI1% Path to the first alarmed event image, suitable for use in img tags + * %EPIM% Path to the (first) event image with the highest score, suitable for use in img tags + * %EIMOD% Path to image containing object detection, suitable for use in img tags + * %EI1% Attach first alarmed event image + * %EIM% Attach (first) event image with the highest score + * %EV% Attach event mpeg video + * %MN% Name of the monitor + * %MET% Total number of events for the monitor + * %MEH% Number of events for the monitor in the last hour + * %MED% Number of events for the monitor in the last day + * %MEW% Number of events for the monitor in the last week + * %MEM% Number of events for the monitor in the last month + * %MEA% Number of archived events for the monitor + * %MP% Path to the monitor window + * %MPS% Path to the monitor stream + * %MPI% Path to the monitor recent image + * %FN% Name of the current filter that matched + * %FP% Path to the current filter that matched + * %ZP% Path to your ZoneMinder console Finally you can also specify a script which is run on each matched event. This script should be readable and executable by your web server user. It will get run once per event and the relative path to the directory containing the event in question. Normally this will be of the form / so from this path you can derive both the monitor name and event id and perform any action you wish. Note that arbitrary commands are not allowed to be specified in the filter, for security the only thing it may contain is the full path to an executable. What that contains is entirely up to you however. @@ -87,15 +100,8 @@ Here is what the filter window looks like Saving filters ----------------- -.. image:: images/filter-save.png - :width: 400px - When saving filters, if you want the filter to run in the background make sure you select the "Run filter in background" option. When checked, ZoneMinder will make sure the filter is checked regularly. For example, if you want to be notified of new events by email, you should make sure this is checked. Filters that are configured to run in the background have a "*" next to it. -For example: - -.. image:: images/filter-choosefilter.png - :width: 400px How filters actually work -------------------------- diff --git a/docs/userguide/gettingstarted.rst b/docs/userguide/gettingstarted.rst index 713a667b5..99f2ea4ba 100644 --- a/docs/userguide/gettingstarted.rst +++ b/docs/userguide/gettingstarted.rst @@ -5,32 +5,146 @@ Having followed the :doc:`/installationguide/index` for your distribution you sh .. image:: ../installationguide/images/zm_first_screen_post_install.png +.. _timezone_config: + +Setting Timezone +^^^^^^^^^^^^^^^^^ +Previous versions of ZoneMinder required the user to set up Timezone correctly in ``php.ini``. This is no longer the case. Starting 1.34, ZoneMinder allows you to specify the TimeZone in the UI. Please make sure it is set up correctly. The Timezone can be changed by selecting ``Options->System->Timezone`` + +.. image:: images/getting-started-timezone.png Enabling Authentication ^^^^^^^^^^^^^^^^^^^^^^^ We strongly recommend enabling authentication right away. There are some situations where certain users don't enable authentication, such as instances where the server is in a LAN not directly exposed to the Internet, and is only accessible via VPN etc., but in most cases, authentication should be enabled. So let's do that right away. -* Click on the Options link on the top right corner of the web interface -* You will now be presented with a screen full of options. Click on the "System" tab +* Click on the Options link on the top bar of the web interface +* You will now be presented with a sidebar full of options. Click on the "System" link .. image:: images/getting-started-enable-auth.png * The relevant portions to change are marked in red above * Enable OPT_USE_AUTH - this automatically switches to authentication mode with a default user (more on that later) * Select a random string for AUTH_HASH_SECRET - this is used to make the authentication logic more secure, so - please generate your own string and please don't use the same value in the example. + please generate your own string and make sure it is sufficiently randomized and long. Note that if you plan to use APIs with ZoneMinder (needed by zmNinja/other apps), it is mandatory that you have this field populated * The other options highlighed above should already be set, but if not, please make sure they are +* Note that if you are planning to use zmNinja and plan to use ZM authentication, you must also: + + * set ``AUTH_RELAY`` to hashed + * Enable ``AUTH_HASH_LOGINS`` + * Click on Save at the bottom and that's it! The next time you refresh that page, you will now be presented with a login screen. Job well done! .. image:: images/getting-started-login.png -.. NOTE:: The default login/password is "admin/admin" +.. note:: The default login/password is "admin/admin" -Switching to flat theme -^^^^^^^^^^^^^^^^^^^^^^^ -What you see is what is called a "classic" skin. Zoneminder has a host of configuration options that you can customize over time. This guide is meant to get you started the easiest possible way, so we will not go into all the details. However, it is worthwhile to note that Zoneminder also has a 'flat' theme that depending on your preferences may look more modern. So let's use that as an example of introducing you to the Options menu +Understanding the Web Console +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Before we proceed, lets spend a few minutes understanding the key functions of the web console. +For the sake of illustration, we are going to use a populated zoneminder configuration with several monitors and events. + +.. image:: images/getting-started-understand-console.png + +This screen is called the "console" screen in ZoneMinder and shows a summary of your monitors, associated events and more information. + +* **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 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. +* **G**: Montage Review allows you to simultaneously view past events for different monitors. Note that this is a very resource intensive page and its performance will vary based on your system capabilities. +* **H**: Audit Events Report is more of a power user feature. This option looks for recording gaps in events and recording issues in mp4 files. +* **I**: This is the user you are currently logged in as. +* **J**: ZoneMinder allows you to maintain "run states". If you click on the "Running" text, ZoneMinder brings up a popup that allows you to define additional "states" (referred to as runstates). A runstate is essentially a snapshot that records the state of each monitor and you can switch between states easily. For example, you might have a run state defined that switches all monitors to "monitor" mode in which they are not recording anything while another state that sets some of the monitors to "modect". Why would you want this? A great example is to disable recording when you are at home and enable when you are away, based on time of day or other triggers. You can switch states by selecting an appropriate state manually, or do it automatically via cron jobs, for example. An example of using cron to automatically switch is provided in the :ref:`FAQ `. More esoteric examples of switching run states based on phone location can be found `here `__. + + +Here is an example of multiple run states that I've defined. Each one of these runstates changes the mode of specific monitors depending on time of day and other conditions. Use your imagination to decide which conditions require state changes. + +.. image:: images/runstates.png + +* **K**: This line shows you system health information +* **L**: This defines how Zoneminder will record events. There are various modes. In brief Modect == record if a motion is detected,Record = always record 24x7, Mocord = always record PLUS detect motion, Monitor = just provide a live view but don't record anytime, Nodect = Don't record till an external entity via zmtrigger tells Zoneminder to (this is advanced usage). +* **M**: This is the "source" column that tells you the type of the camera - if its an IP camera, a USB camera or more. In this example, they are all IP cameras. Green means the monitor is running. Red means there is something wrong with that camera. +* **N**: This is the core of ZoneMinder - recording events. It gives you a count of how many events were recorded over the hour, day, week, month. +* **O**: These are the "Zones". Zones are areas within the camera that you mark as 'hotspots' for motion detection. Simply put, when you first configure your monitors (cameras), by default Zoneminder uses the entire field of view of the camera to detect motion. You may not want this. You may want to create "zones" specifically for detecting motion and ignore others. For example, lets consider a room with a fan that spins. You surely don't want to consider the fan moving continuously a reason for triggering a record? Probably not - in that case, you'd leave the fan out while making your zones. +* **P**: This is a "visual filter" which lets you 'filter' the console display based on text you enter. While this may not be particularly useful for small systems, ZoneMinder is also used in mega-installations will well over 200+ cameras and this visual filter helps reduce the monitors you are seeing at one time. + +Adding Monitors +^^^^^^^^^^^^^^^ +Now that we have a basic understanding of the web console, lets go about adding a new camera (monitor). For this example, lets assume we have an IP camera that streams RTSP at LAN IP address 192.168.1.33. + +.. sidebar:: Note + + This is meant to be a simple example. For a more detailed explanation of other options available when creating a monitor, please see :doc:`/userguide/definemonitor` + +The first thing we will need to know is how to access that camera's video feed. You will need to consult your camera's manual or check their forum. Zoneminder community users also have a frequently updated list right `here `__ that lists information about many cameras. If you don't find your list there and can't seem to find it elsewhere, feel free to register and ask in the `user forums `__. + +The camera we are using as an example here is a Foscam 9831W which is a 1280x960 RTSP camera, and the URL to access it's feed is *username:password@IPADDRESS:PORT/videoMain* + +Let's get started: + +Click on the "Add" button below: + +.. image:: images/getting-started-modern-look.png + :width: 600px + +This brings up the new monitor window: + +.. image:: images/getting-started-add-monitor-general.png + :width: 600px + +* We've given it a name of 'Garage', because, well, its better than Monitor-1 and this is my Garage camera. + +* There are various source types. As a brief introduction you'd want to use 'Local' if your camera is physically attached to your ZM server (like a USB camera, for example), and one of 'Remote', 'FFMpeg', 'Libvlc' or 'cURL' for a remote camera (not necessarily, but usually). For this example, let's go with 'FFMpeg'. + +.. note:: + As a thumb rule, if you have a camera accessible via IP and it does HTTP or RTSP, + start with FFMpeg first and libvlc if it doesn't work (:doc:`/userguide/definemonitor` + covers other modes in more details). If you are wondering what 'File' does, well, ZoneMinder was + built with compatibility in mind. Take a look at `this post + `__ to see how file can be used for leisure reading. + +* In this example, the Function is 'Modect', which means it will start recording if motion is detected on that camera feed. The parameters for what constitutes motion detected is specific in :doc:`definezone` + +* In Analysis FPS, we've put in 5FPS here. Note that you should not put an FPS that is greater than the camera FPS. In my case, 5FPS is sufficient for my needs + +.. note:: + Leave Maximum FPS and Alarm Maximum FPS **empty** if you are configuring an IP camera. In older versions of ZoneMinder, you were encouraged to put a value here, but that is no longer recommended. Infact, if you see your feed going much slower than the feed is supposed to go, or you get a lot of buffering/display issues, make sure this is empty. If you need to control camera FPS, please do it directly on the camera (via its own web interface, for example) + + +* We are done for the General tab. Let's move to the next tab + +.. image:: images/getting-started-add-monitor-source.png + :width: 800px + +* Let's select a protocol of RTSP and a remote method of RTP/RTSP (this is an RTSP camera) +* Note that starting ZM 1.34, GPUs are supported. In my case, I have an NVIDIA GeForce GTX1050i. These ``cuda`` and ``cuvid`` parameters are what my system supports to use the NVIDIA hardware decoder and GPU resources. If you don't have a GPU, or don't know how to configure your ffmpeg to support it, leave it empty for now. In future, we will add a section on how to set up a GPU + +**NOTE**: It is entirely possible that ``cuda`` and ``cuvid`` don't work for you and you need different values. Isaac uses ``cuda`` in ``DecoderHWAccelName`` and leaves ``DecoderHWAccelDevice`` empty. Try that too. + +.. todo:: + add GPU docs + +That's pretty much it. Click on Save. We are not going to explore the other tabs in this simple guide. + +You now have a configured monitor: + +.. image:: images/getting-started-add-monitor-modect-ready.png + + +And then, finally, to see if everything works, if you click on the garage monitor you just added, you should be able to see its live feed. If you don't, inspect your webserver logs and your ZoneMinder logs to see what is going on. + + +Switching to another theme +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. todo:: + Fix theme text after I clearly understand that System->CSS is doing + +When you first install ZoneMinder, you see is what is called a "classic" skin. Zoneminder has a host of configuration options that you can customize over time. This guide is meant to get you started the easiest possible way, so we will not go into all the details. However, it is worthwhile to note that Zoneminder also has a 'flat' theme that depending on your preferences may look more modern. So let's use that as an example of introducing you to the Options menu * Click on the Options link on the top right of the web interface in the image above * This will bring you to the options window as shown below. Click on the "System" tab and then select the @@ -51,98 +165,6 @@ Congratulations! You now have a modern looking interface. .. image:: images/getting-started-modern-look.png -Understanding the Web Console -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Before we proceed, lets spend a few minutes understanding the key functions of the web console. -For the sake of illustration, we are going to use a populated zoneminder configuration with several monitors and events. -Obviously, this does not reflect your current web console - which is essentially void of any useful information till now, -as we are yet to add things. Let's take a small break and understand what the various functions are before we configure our -own empty screen. - -.. image:: images/getting-started-understand-console.png - -* **A**: This is the username that is logged in. You are logged in as 'admin' here -* **B**: Click here to explore the various options of ZoneMinder and how to configure them. You already used this to enable authentication and change style above. Over time, you will find this to have many other things you will want to customize. -* **C**: This link, when clicked, opens up a color coded log window of what is going on in Zoneminder and often gives you good insight into what is going wrong or right. Note that the color here is red - that is an indication that some error occurred in ZoneMinder. You should click it and investigate. -* **D**: This is the core of ZoneMinder - recording events. It gives you a count of how many events were recorded over the hour, day, week, month. -* **E**: These are the "Zones". Zones are areas within the camera that you mark as 'hotspots' for motion detection. Simply put, when you first configure your monitors (cameras), by default Zoneminder uses the entire field of view of the camera to detect motion. You may not want this. You may want to create "zones" specifically for detecting motion and ignore others. For example, lets consider a room with a fan that spins. You surely don't want to consider the fan moving continuously a reason for triggering a record? Probably not - in that case, you'd leave the fan out while making your zones. -* **F**: This is the "source" column that tells you the type of the camera - if its an IP camera, a USB camera or more. In this example, they are all IP cameras. Note the color red on item F ? Well that means there is something wrong with that camera. No wonder the log also shows red. Good indication for you to tap on logs and investigate -* **G**: This defines how Zoneminder will record events. There are various modes. In brief Modect == record if a motion is detected,Record = always record 24x7, Mocord = always record PLUS detect motion, Monitor = just provide a live view but don't record anytime, Nodect = Don't record till an external entity via zmtrigger tells Zoneminder to (this is advanced usage). -* **H**: If you click on these links you can view a "Montage" of all your configured monitors or cycle through each one -* **I**: One of the most often missed features is the ability of ZoneMinder to maintain "run states". If you click on the "Running" text, ZoneMinder brings up a popup that allows you to define additional "states" (referred to as runstates). A runstate is essentially a snapshot that records the state of each monitor and you can switch between states easily. For example, you might have a run state defined that switches all monitors to "monitor" mode in which they are not recording anything while another state that sets some of the monitors to "modect". Why would you want this? A great example is to disable recording when you are at home and enable when you are away, based on time of day or other triggers. You can switch states by selecting an appropriate state manually, or do it automatically via cron jobs, for example. An example of using cron to automatically switch is provided in the :ref:`FAQ `. More esoteric examples of switching run states based on phone location can be found `here `__. - -Here is an example of multiple run states that I've defined. Each one of these runstates changes the mode of specific monitors depending on time of day and other conditions. Use your imagination to decide which conditions require state changes. - -.. image:: images/runstates.png - - - -Adding Monitors -^^^^^^^^^^^^^^^ -Now that we have a basic understanding of the web console, lets go about adding a new camera (monitor). For this example, lets assume we have an IP camera that streams RTSP at LAN IP address 192.168.1.33. - -The first thing we will need to know is how to access that camera's video feed. You will need to consult your camera's manual or check their forum. Zoneminder community users also have a frequently updated list right `here `__ that lists information about many cameras. If you don't find your list there and can't seem to find it elsewhere, feel free to register and ask in the `user foums `__. - -The camera we are using as an example here is a Foscam 9831W which is a 1280x960 RTSP camera, and the URL to access it's feed is *username:password@IPADDRESS:PORT/videoMain* - -Let's get started: - -Click on the "Add new monitor" button below: - -.. image:: images/getting-started-modern-look.png - -This brings up the new monitor window: - -.. image:: images/getting-started-add-monitor-general.png - :width: 800px - -* We've given it a name of 'Garage', because, well, its better than Monitor-1 and this is my Garage camera. - -* There are various source types. As a brief introduction you'd want to use 'Local' if your camera is physically attached to your ZM server (like a USB camera, for example), and one of 'Remote', 'FFMpeg', 'Libvlc' or 'cURL' for a remote camera (not necessarily, but usually). For this example, let's go with 'Remote'. - -.. NOTE:: - As a thumb rule, if you have a camera accessible via IP and it does HTTP or RTSP, - start with Remote, then try FFMpeg and libvlc if it doesn't work (:doc:`/userguide/definemonitor` - covers other modes in more details). If you are wondering what 'File' does, well, ZoneMinder was - built with compatibility in mind. Take a look at `this post - `__ to see how file can be used for leisure reading. - -* Let's leave the Function as 'Monitor' just so we can use this as an example to change it later another way. Practically, feel free to select your mode right now - Modect, Record etc depending on what you want ZoneMinder to do with this camera - -* We've put in MaxFPS and AlarmFPS as 20 here. **You can leave this empty too**. Whatever you do here, *it's important to make sure these values are higher than the FPS of the camera*. The reason we've added a value here is that as of Aug 2015, if a camera goes offline, ZoneMinder eats up a lot of CPU trying to reach it and putting a larger value here than the actual FPS helps in that specific situation. - -.. NOTE:: - We strongly recommend not putting in a lower FPS here that the one configured inside your camera. - Zoneminder should not be used to manage camera frame rate. That always causes many problems. It's - much better you set the value directly in-camera and either leave this blank or specify a higher FPS - here. In this case, our actual camera FPS is 3 and we've set this value here to 10. - -* We are done for the General tab. Let's move to the next tab - -.. image:: images/getting-started-add-monitor-source.png - :width: 800px - -* Let's select a protocol of RTSP and a remote method of RTP/RTSP (this is an RTSP camera) -* The other boxes are mostly self-explanatory - -That's pretty much it. Click on Save. We are not going to explore the other tabs in this simple guide. - -You now have a configured monitor: - -.. image:: images/getting-started-add-monitor-orange.png - -If you want to change its mode from Monitor to say, Modect (Motion Detect), later all you need to do is click on the Function column that says 'Monitor' and change it to 'Modect' like so: - - -.. image:: images/getting-started-add-monitor-modect.png - -and we now have: - -.. image:: images/getting-started-add-monitor-modect-ready.png - -And then, finally, to see if everything works, lets click on the monitor name ('Garage' in this example) and that should bring up a live feed just like this: - -.. image:: images/getting-started-add-monitor-live.png Conclusion diff --git a/docs/userguide/images/filter-button.png b/docs/userguide/images/filter-button.png deleted file mode 100644 index a480b1900..000000000 Binary files a/docs/userguide/images/filter-button.png and /dev/null differ diff --git a/docs/userguide/images/filter-filterview.png b/docs/userguide/images/filter-filterview.png index 561996668..e09f34b4d 100644 Binary files a/docs/userguide/images/filter-filterview.png and b/docs/userguide/images/filter-filterview.png differ diff --git a/docs/userguide/images/filter-save.png b/docs/userguide/images/filter-save.png deleted file mode 100644 index 96db574c6..000000000 Binary files a/docs/userguide/images/filter-save.png and /dev/null differ diff --git a/docs/userguide/images/getting-started-add-monitor-general.png b/docs/userguide/images/getting-started-add-monitor-general.png index 9b722907d..cf4c93487 100644 Binary files a/docs/userguide/images/getting-started-add-monitor-general.png and b/docs/userguide/images/getting-started-add-monitor-general.png differ diff --git a/docs/userguide/images/getting-started-add-monitor-live.png b/docs/userguide/images/getting-started-add-monitor-live.png deleted file mode 100644 index a1d7f4100..000000000 Binary files a/docs/userguide/images/getting-started-add-monitor-live.png and /dev/null differ diff --git a/docs/userguide/images/getting-started-add-monitor-modect-ready.png b/docs/userguide/images/getting-started-add-monitor-modect-ready.png index 338a0e097..fcac21140 100644 Binary files a/docs/userguide/images/getting-started-add-monitor-modect-ready.png and b/docs/userguide/images/getting-started-add-monitor-modect-ready.png differ diff --git a/docs/userguide/images/getting-started-add-monitor-modect.png b/docs/userguide/images/getting-started-add-monitor-modect.png deleted file mode 100644 index bc6795b37..000000000 Binary files a/docs/userguide/images/getting-started-add-monitor-modect.png and /dev/null differ diff --git a/docs/userguide/images/getting-started-add-monitor-orange.png b/docs/userguide/images/getting-started-add-monitor-orange.png deleted file mode 100644 index 9d5e227aa..000000000 Binary files a/docs/userguide/images/getting-started-add-monitor-orange.png and /dev/null differ diff --git a/docs/userguide/images/getting-started-add-monitor-source.png b/docs/userguide/images/getting-started-add-monitor-source.png index e8b19925e..a3b9324f5 100644 Binary files a/docs/userguide/images/getting-started-add-monitor-source.png and b/docs/userguide/images/getting-started-add-monitor-source.png differ diff --git a/docs/userguide/images/getting-started-audit-event.png b/docs/userguide/images/getting-started-audit-event.png new file mode 100644 index 000000000..91e1386d8 Binary files /dev/null and b/docs/userguide/images/getting-started-audit-event.png differ diff --git a/docs/userguide/images/getting-started-enable-auth.png b/docs/userguide/images/getting-started-enable-auth.png index 9160ffecc..ec376c478 100644 Binary files a/docs/userguide/images/getting-started-enable-auth.png and b/docs/userguide/images/getting-started-enable-auth.png differ diff --git a/docs/userguide/images/getting-started-login.png b/docs/userguide/images/getting-started-login.png index d981048c0..6b6c8e05d 100644 Binary files a/docs/userguide/images/getting-started-login.png and b/docs/userguide/images/getting-started-login.png differ diff --git a/docs/userguide/images/getting-started-modern-look.png b/docs/userguide/images/getting-started-modern-look.png index 34dbcdfc8..6cb62718c 100644 Binary files a/docs/userguide/images/getting-started-modern-look.png and b/docs/userguide/images/getting-started-modern-look.png differ diff --git a/docs/userguide/images/getting-started-timezone.png b/docs/userguide/images/getting-started-timezone.png new file mode 100644 index 000000000..e372eb8b0 Binary files /dev/null and b/docs/userguide/images/getting-started-timezone.png differ diff --git a/docs/userguide/images/getting-started-understand-console.png b/docs/userguide/images/getting-started-understand-console.png index f332b4977..89c735992 100644 Binary files a/docs/userguide/images/getting-started-understand-console.png and b/docs/userguide/images/getting-started-understand-console.png differ diff --git a/docs/userguide/images/viewevents-stream.png b/docs/userguide/images/viewevents-stream.png index 672a74e4d..a1448e0bc 100644 Binary files a/docs/userguide/images/viewevents-stream.png and b/docs/userguide/images/viewevents-stream.png differ diff --git a/docs/userguide/images/viewmonitor-main.png b/docs/userguide/images/viewmonitor-main.png index 74354f0b0..80791ebb6 100644 Binary files a/docs/userguide/images/viewmonitor-main.png and b/docs/userguide/images/viewmonitor-main.png differ diff --git a/docs/userguide/images/viewmonitor-stream.png b/docs/userguide/images/viewmonitor-stream.png index 126a67bb2..58c35952c 100644 Binary files a/docs/userguide/images/viewmonitor-stream.png and b/docs/userguide/images/viewmonitor-stream.png differ diff --git a/docs/userguide/images/zm-system-overview.jpg b/docs/userguide/images/zm-system-overview.jpg index de02dcf9e..61594a337 100644 Binary files a/docs/userguide/images/zm-system-overview.jpg and b/docs/userguide/images/zm-system-overview.jpg differ diff --git a/docs/userguide/images/zm-system-overview.xml b/docs/userguide/images/zm-system-overview.xml index e4314666a..8cbd3b3ca 100644 --- a/docs/userguide/images/zm-system-overview.xml +++ b/docs/userguide/images/zm-system-overview.xml @@ -1,2 +1 @@ - - \ No newline at end of file +7V1Zk6s2Fv41XZU82AWI9bGXdCZTuVW3pieVe/MyRRvazTS2HMC9/fqRAGF0JGyBhU33XKfS1yxmOct3VkkX6Hr1+msWbh6/4ChOLywjer1ANxeWZZqBTf6he96qPR4yqx3LLInqk3Y77pL3uN5p1Hu3SRTn3IkFxmmRbPidC7xex4uC2xdmGX7hT3vAKX/XTbiMhR13izAV9/6ZRMUjey832B34R5wsH+tb+5ZbHbgPF0/LDG/X9f0uLPRQfqrDq5Bdq37R/DGM8EtrF/rlAl1nGBfVt9XrdZxS2jKyVb+77TjaPHcWrwuVH1jVD57DdBuzJy6fq3hjtHh5TIr4bhMu6PYL4fcFunosVinZMslX8Y71QzzHWRG/tnbVT/BrjFdxkb2RU+qjNqNGLS1+vfnSIr1X73tsUd1kJ4Y1u5fNpXevTL7Uby2nABIo8NeXa/MMVHBMh6OCaYhkcCVUQBqIYMuIcA5REIhgn44IjowI6wkQoUGLExDBPwwI8Tq6pBhLttZ4HfOvH78mxTfy3Zg79dZ3ukW/r8mjtA7Rze/sV+voNqHPVJ4ZhfljHB2iZ4632SLmWFeE2TIuOJGOIw7pRZq3aOpIaMr2ZXEaFskzbx9khK7v8BUn5Hl3LAUQ17CUXaJ6m/pXbcCGF/L4CzXAyC5U0UC4UMn35rWVRCGQ6MPlOZDRtsE7S5BRxjsd+sA8Ep4KZ7GSkAoSaByNCqaMCufARkgFGTaORgUFb2mC4MiY10ZHJtYH4ZGRvQt2FOFTHSltu8P49UVKO3Dkj6wfKS0RJHoKRsP+NvN3YvKtJUCtI5xg5OR9CnaLRRrmebJgu+vTlEUmECUG9RQYB6KVM5bAIIAI7E69BcY5cCGNAiPi6ccWGIYnQzwwRn4XSIwzmsRAG+IMlRj3wIU0SszRtmdqEiOxSs6RRskdS2Ics+NOvd13y9l/IY0SI2Y2BIlpyQd1wZJFmP4e3sfpV5wnRYLX5Ng9Lgq8ap1wmSZLeqDAm5L3GX5qkmRWs+capzgrb4IMwzduqfg8EBFp7a8TY+QXLAnG+TdGdWRDH3X1uqSJxnmCc2+eLPA6nz8nUYz/Q76S58rD6mGvMlzUX29mNDukxeV0hfDDE8QMScTMdLrFTNXlZHnFH0zUzUTbPx0TvR9MHIWJTbrqFEwUM2Pvq5ewWDzON6nAzlMkCX2OFoj5W+0koSPSQkcgjETT8r6KFuchhA2ypSclhJg4FyjQ8sp2LpM0KWC7FkwLkO9f4ywhDxZn9b5OkrW9KyatU0l/2gGvuMHQ9KfNS73lIP5C+vwnJJreFV4nBQFNQv4ML+I8v7DctKBQmTyTr0v6tYSECC/ZIXKb1lFBOoiQF7w8ZHGevIf35Qklv9FVWAP9grCcyoFgAVZJFNEfXKXUZlw1RUgZxrexvw4TRJFigl0XWOvHudhlOpTVc2bMDWYAa6bNLC1CZUGLzl8APzzk8dFCIBaF+kKcEr37w7/Ba1RgCqDnIYkldC0NqKfgz1Dguas34/Qev/yy23FV7iAHHnGWvON1EaZVqLkfJzsC2MBi2xAp5WGt5aiCKFLNeZ0GRG0fGDpzKIiaMG0xWmaUOWw9QZTADvFd0w+OoZ5GDHVNBslNFKpFqIDzNGP2VS+KiqVEETBU3SRJjqoGBvNCyGwp6Tlzlrh097R03xIiH2DtVHW/8byaqgi4kEbd75fklnJ9mcXxWghuEQxV1dg8LZa6KJgHBvJt13ToX4vni+nOzYAEKZ5b/oVsUu8XAAlHGAtr5LcYDJWpgY+C10xeteC15Xs8Xh/r9GoFZNs6mWoej86uiM49S0sO8JxMy547AfLGKhe4NlHt1sffd/feGi28zHgaLUY/H0ujraM1+lj3CvG84n+vR5cV6jyT0WVP1OWeRT8HtnWgsXUZeF4wbzhYb1177jPzTv+CyFyjGouZrI+lxujsamzythwkHfXosUpWZV+Fn1deugdn4ZpqFdRinTX9iXjTtgu0yxiopvBC7ni5EVmvdSsV0mK8+/eWjgi5ogo6qxXwkpyRxg/F7uguQVJdJd+Ea+llHvC6mOXlqBt6FdPYvJJ/yl8aabKOZ4xB5dG5Jd7ich2mb+T3dGwNTeWscFns2+Vrqnvzz9M7jcMXMGuJl0CGOiTJcrWYnP2QVglJcl5pCDvFvUddCgG3zxUslC8pS9l7LJTySIZj+xGVoWRPQ+tYjUOod5cQUGjeN2dD1fR3mcHI25DfuH8JzOWuO17y1pElcD4IQC3CTbHNKEDVrs515UBQ/aUOKKbQlYWrOP8BWnvKSqcELTEncSPSm/WhLN6IHEQl3c5SfyNxCE8oSbeqLcMRDa0oTr+ITwLvbQwXsb5du8NZ8YiXmJj7dgFPEc95IzB1FxIZIDU+tEkBjmc1YWm6A6IJQcO31mkbekK+54HhYMnA2Ptcwvn19k7sqicYbC/6dcMMdztOLH6MfGCggWefxpGA9yXipMWRUHyfEfwKWVPFB/Er8gKXXgXzHSpPIkryJ3ohyw1X1PxUf6nHERZhSpuBjIQ+0c3VD2+jbUQD3ohKRjSahjmSuyGmW95XRZYsl3F2nkZGmGls4Lrd0zNSI6OjMu5be0vPIT+kI90c+M2OrpYfSUBKvt+8tjfeehoGMOTcO6evIsjK4Kx0YM4De/fhY0vyuHOj/XFHcWRcMJCHeWLaHBOx/4QAaZwRr5YieKX0bfxe3+f0H4rt/B5Jr1LpdVA4p234lLELnEXJeroNoJzWLale1t/BqICbX+h/9JwsjBJy54NRFEMRPR1PTSP3sX49n4ebmWNk710x5fZnfE923MXZMxWufuZEiUc1H5TZ09siIdAoaLtoLtokJJubR4dRcmWzDpDtX//4jfz9afO4KXWW+ln/vPv5k5AYnZbEYtKlJPHl198+Bz2RI/qUMmKyyvRRxFTJy0zFifJNv8uJ+iQlQWR6clHo3zPJX8iCtUVt+Rxwn0CvG+Sq5GfOJp+9xqXrLCgxDJxw+28jukyU4cx3yqlJ40DcoC/L44pZnm1OPaHbMFol4txBLLO/XaWXCzpAYPBI2MkZJY+NzmlKdmojyGHvwSCjpCuyZ7TWqfKSyY462/zVdFk2q4Q1KV22QLBrw/5N5fwtKDMgWK/QZZYAZtj1YE5tZkmMzt9Xm6flmYYT+3xOEklmuhprODG71aQMdCdNJ2wv7eCAaijby65wQrOOwSmJmIHXpWOeylicqYQmVtv3M+aehlRuW1YZ3uxN77rnHS8P5NceOtTTBTNUIcVJ8PrKL3xgpDl08cQ8BZ93vc3i3Y6w2OaCeE800drX+WSarCXH6hiWHrzluT9Kf7SnK7uiz5HthJupmEHoywQDXU2LNQOwzATEI12upgAjejtUPIUMyAeeiSmlEnS/Te8B3nTKaY8Q1gcSwJrQ2rV6W5RkHVMvebKREVVxjTZ0cOzr6PTwaaMH7OWo0hFGvsiSTdEq11UX7ajXMfKvcUFBZfykePUSN3tDYHU++qDpB0nmZbUkiKQlvOk3ufwgl7LxGi3oNU4drL3AI66ujZBjuYZpOmB6L2TODd/1DMcyLGQKeqWK5PQuYIJlT62oPwBu/SnGs8Mmkig3d0FHuXUw6mgHFAzEhKiDS1WdtanEg8ECzD8qCxnMTClmmfu6C35HdK7LXfDFqLlpFvmc0QXTWC3Rhe0aYOIvpEVOZ3DSGiheWuINXww5tXgd4ZpSCWVRRfqCbkXxc0KAQtEFmVIXaKcUavBVPKcj6mjX8iU+J0LdEqW8Do4YKbyv6mmpzpOY9gzgEQSnaw8NpmzIxeqwkkF2p+X+uT5vy+yh3Qoe7ChSjNUHQGQgyyprgMhFuIqzkP58Ey+SByIX5WRxBV7QOeGMvG6pM37axFlacnrxFC7jn1UB9EN0Mvlg7pAmN9KSx0AyxaMWfZfZvmnqu8d57qCJRH2uR4b3bW88UEUIpsIwa6+4NtGASUngndDQcNDqeGTdnjqsIWvu8A6m2HrXKXgTMTk+mHNucJUUiqOpOGlGbykCD8waZLRJkdiy1Jiir//+S5CoTxL0BcfPr1MfJe4XNFvWcaKqNaQLRKde4CnL76bJ+mmvtqsOQQXDXeHEGGrpqf2GbCJ44vrO3HR3H9DNagdz1PpYIO2oDDY+P0YpGC19GSgsvnJucRkuE7uZZXmm8TE3Kzdrd2Ga6TabrrKh3dngQmOuiCpbA2S7icIiLnMCxoz8v91Q4C3HBtOhFP1Cj/ayKYGmNEqzanmjiJLSnSlhqpZIQtbcR4xVMWiU7TQiMwRXs/AlmZiRxpg085pNytPtX1PxVAMzJkAT7uhFcPjq0G4tOHHIiJPPNePqW3LUzMdvVAr6aXuoGiXSUubwDDA+aMYWCj5WrhC47BhtVabRPRuHahLPNGVZPOK3xPlbXsSrvr0VEV5sVyW3TtBfocEgWCZvEBxxeIfb02lStwc9F2PJ4yUlLWcRxmnj3Ruy8ItRq07pd6K2OTh/kwEWxVAeoQGXDR0puybcp35g1eeqS5260igNvTinL9xGSfFhfT7L4lXcNEUdH8/nE53oU+v44ZXroYOnnHpvxGXKmCCsmzLUx0OgV3qkVKn4vN7ex4Ln16kybZBg6gpbNA5DlPcK9ljzfFqLLJpgkLCQCFHOqMCV2EcaNNJMeQ7uo0/oPne7d/wWX2hvumlqv2+AiAcavXXYOVMWivzg115+wRGKTd/aKfj1uVcnH4VfQhuXKWbYR+PX516IfBR+wQXUZMtLj8YvhQETn4pfY5Q/LAOMdzDURi5pYaBC4PapGKhD4UC5yhcD7bHYZSkEKT/YBefLAIGjuGLAaOyyfrCrNxqCINGxTsivKTbpHZ29ZsHyRLIAHkjkOENnBvagpCjOejckULfEQP2xKDZldemWTulQdnzPF0RNrdv3lSA1n6UuaWnsxDMCMOvYpFagbVYtFVeGJ9hoGff0Xd5X0aKsGFyX6lNe4D4uX6HsnCrPTB4owbIKVA8LxuccKtWs1cSUVVKeaPrZufqEhhpkuHq6u/vmEpFKgm/ev17++Pv+edYwuAPc5QvLlPrWGMfdOb9jaotLBv43Loq3u2pQfLgtMM/ePZ0o/WcN4pYuFZHfUgR+ZYVTJfj378n7Q57hv+4WT4unIMJf3v+eyTTqfRU/k5da44KOoilDqwEVuMZh2aXGBcmU0K9TWF04tt3xJHMeN++juyWtg3r/n7msoxjpeS5gpCtj5DjupJyNaNo+5tywgraf2b2QxZ75BoZjnDD5wAHQ2w80Ahaey+d14fS4QytfDl0DqXM1DGSCPhBdy1/A6RI0T2PQoSmiz10ai4vdAMuqa+s+2zVs3X75nbIQ46cP0xyovJKFstPNq80+HNrXNqhtfYsZL6WzYA4GAw5x2Mlmhmk73+50alK+4CimZ/wP \ No newline at end of file diff --git a/docs/userguide/index.rst b/docs/userguide/index.rst index 35bfcf39d..905a87292 100644 --- a/docs/userguide/index.rst +++ b/docs/userguide/index.rst @@ -15,4 +15,5 @@ User Guide cameracontrol mobile logging + configfiles diff --git a/docs/userguide/logging.rst b/docs/userguide/logging.rst index 4256079ae..a6dfdb0c0 100644 --- a/docs/userguide/logging.rst +++ b/docs/userguide/logging.rst @@ -1,18 +1,33 @@ Logging ======= -Most components of ZoneMinder can emit informational, warning, error and debug messages in a standard format. These messages can be logged in one or more locations. By default all messages produced by scripts are logged in @@ -95,35 +62,35 @@ html ul.tabs li.active, html ul.tabs li.active a:hover { Id()); $otherlinks = ''; - if( $exportFrames ) $otherlinks .= ''.translate('Frames').','; - if( $exportImages ) $otherlinks .= ''.translate('Images').','; - $otherlinks = substr($otherlinks,0,-1); + if ( $exportFrames ) $otherlinks .= ' '.translate('Frames').','; + if ( $exportImages ) $otherlinks .= ' '.translate('Images').','; + $otherlinks = substr($otherlinks, 0, -1); ?>
-

: Name()) ?> ()

+

Name()).( (!empty($otherlinks)) ? ' ('.$otherlinks.') ' : '' ) ?>

- + - + - +
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') ?>
@@ -134,21 +101,21 @@ function exportEventDetail($event, $exportFrames, $exportImages) { } function exportEventFrames($event, $exportDetail, $exportImages) { - $sql = 'SELECT *, unix_timestamp( TimeStamp ) AS UnixTimeStamp FROM Frames WHERE EventID = ? ORDER BY FrameId'; + $sql = 'SELECT *, unix_timestamp(TimeStamp) AS UnixTimeStamp FROM Frames WHERE EventID = ? ORDER BY FrameId'; $frames = dbFetchAll($sql, NULL, array($event->Id())); ob_start(); exportHeader(translate('Frames').' '.$event->Id()); $otherlinks = ''; - if ( $exportDetail ) $otherlinks .= ''.translate('Event').','; - if ( $exportImages ) $otherlinks .= ''.translate('Images').','; - $otherlinks = substr($otherlinks,0,-1); + if ( $exportDetail ) $otherlinks .= ' '.translate('Event').','; + if ( $exportImages ) $otherlinks .= ' '.translate('Images').','; + $otherlinks = substr($otherlinks, 0, -1); ?>
-

: Name()) ?> ()

+

Name()).( (!empty($otherlinks)) ? ' ('.$otherlinks.')':'') ?>

@@ -208,35 +175,54 @@ function exportEventFrames($event, $exportDetail, $exportImages) { Id()); $otherlinks = ''; - if( $exportDetail ) $otherlinks .= ''.translate('Event').','; - if( $exportFrames ) $otherlinks .= ''.translate('Frames').','; - $otherlinks = substr($otherlinks,0,-1); + if ( $exportDetail ) $otherlinks .= ' '.translate('Event').','; + if ( $exportFrames ) $otherlinks .= ' '.translate('Frames').','; + $otherlinks = substr($otherlinks, 0, -1); $filelist = array_keys($myfilelist); - sort($filelist,SORT_NUMERIC); + sort($filelist, SORT_NUMERIC); $slides = '"'.implode('","',$filelist).'"'; $listcount = count($filelist); ?> -

: Name()) ?> ()

+

Name()).( (!empty($otherlinks)) ? ' ('.$otherlinks.') ' : '' ) ?>

DefaultVideo() ) { @@ -247,12 +233,12 @@ 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(); - } + } # end if passthrough ?>
-
@@ -582,48 +568,57 @@ else if (document.layers) window.onload=start_slider; -
- +'; if ( $Event->SaveJPEGs() ) { -?> - - -<?php echo $Event->Id()?> -Id(); } ?> - -Id().'/zmEventImages.html\');return false;"> +'; + if ( ZM_WEB_LIST_THUMBS ) { + $html .= ''.$Event->Id().' +'; + } else { + $html .= $Event->Id(); + } + $html .= '
+'; } # end if has jpegs if ( $Event->DefaultVideo() ) { if ( ZM_WEB_LIST_THUMBS ) { -?> - - <?php echo $Event->Id()?> - -Id().'/'.$Event->DefaultVideo() .'">'; + $html .= ''.$Event->Id().''; + $html .= '
+ '; } } -?> -
-Id().'/zmEventDetail.html\');return false;">Detail +'; + } + if ( $exportFrames ) { + $html .= 'Frames +'; + } + $html .= ' +'; + return $html; +} // end function eventlist_html -function exportEventImagesMaster($eids) { +function exportEventImagesMaster($eids, $exportDetail, $exportFrames) { ob_start(); exportHeader(translate('Images').' Master'); ?>

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(); + $eventMonitorId[$event->Id()] = $event->MonitorId(); + $eventPath[$event->Id()] = $event->Relative_Path(); } $monitors = array_values(array_flip(array_flip($eventMonitorId))); //unique monitors and reindex the array @@ -631,11 +626,10 @@ function exportEventImagesMaster($eids) { //* if ( !empty($monitors) ) { - $tmp = dbFetchAll('SELECT Id,Name FROM Monitors WHERE Id IN ('.implode(',', $monitors).') '); + $tmp = dbFetchAll('SELECT Id, Name FROM Monitors WHERE Id IN ('.implode(',', $monitors).') '); foreach ( $tmp as $row ) { $monitorNames[$row['Id']] = $row['Name']; } } //*/ - //trigger_error(print_r($monitorNames,1)); ?>
    @@ -647,33 +641,32 @@ function exportEventImagesMaster($eids) { ?>
-
+
+ - - + + - - + + - + DiskSpace(); $event_count += 1; -?> - -'.human_filesize($event->DiskSpace()).''; } unset($event); + echo ' + +'; } # end foreach event ?> - - + + + + + +

All

-
+ "; - echo '

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

'; - foreach ( $eids as $eid ) { - $Event = new ZM\Event($eid); - if ( $Event->MonitorId() == $monitor_id ) { - eventlist_html($Event); + foreach ( $monitors as $monitor_id ) { + echo '
'; + echo '

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

'; + foreach ( $events as $event ) { + if ( $event->MonitorId() == $monitor_id ) { + echo eventlist_html($event, $exportDetail, $exportFrames); } # end if its the right monitor } # end foreach event echo '
'; } # end foreach monitor ?> -
- @@ -683,7 +676,7 @@ function exportEventImagesMaster($eids) { @@ -753,7 +764,14 @@ function loadintoIframe(iframeid, url) { return ob_get_clean(); } -function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $exportVideo, $exportMisc) { +function exportFileList( + $event, + $exportDetail, + $exportFrames, + $exportImages, + $exportVideo, + $exportMisc +) { if ( !canView('Events') or !$event ) { return; @@ -775,29 +793,32 @@ function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $ex if ( $exportDetail ) { $file = 'zmEventDetail.html'; - if ( !($fp = fopen($eventPath.'/'.$file, 'w')) ) { - ZM\Fatal("Can't open event detail export file '$file'"); + if ( $fp = fopen($eventPath.'/'.$file, 'w') ) { + fwrite($fp, exportEventDetail($event, $exportFrames, $exportImages)); + fclose($fp); + $exportFileList[$file] = $file; + } else { + ZM\Error("Can't open event detail export file '$file'"); } - fwrite($fp, exportEventDetail($event, $exportFrames, $exportImages)); - fclose($fp); - $exportFileList[$file] = $event->Id().'/'.$file; } + if ( $exportFrames ) { $file = 'zmEventFrames.html'; - if ( !($fp = fopen($eventPath.'/'.$file, 'w')) ) { - ZM\Fatal("Can't open event frames export file '$file'"); + if ( $fp = fopen($eventPath.'/'.$file, 'w') ) { + fwrite($fp, exportEventFrames($event, $exportDetail, $exportImages)); + fclose($fp); + $exportFileList[$file] = $file; + } else { + ZM\Error("Can't open event frames export file '$file'"); } - fwrite($fp, exportEventFrames($event, $exportDetail, $exportImages)); - fclose($fp); - $exportFileList[$file] = $event->Id().'/'.$file; } if ( $exportImages ) { $filesLeft = array(); $myfilelist = array(); foreach ( $files as $file ) { - if ( preg_match('/-(?:capture|analyse).jpg$/', $file ) ) { - $myfilelist[$file] = $exportFileList[$file] = $event->Id().'/'.$file; + if ( preg_match('/-(?:capture|analyse).jpg$/', $file) ) { + $myfilelist[$file] = $exportFileList[$file] = $file; } else { $filesLeft[$file] = $file; } @@ -806,12 +827,14 @@ function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $ex // create an image slider if ( !empty($myfilelist) ) { - $file = $event->Id().'/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; + $file = 'zmEventImages.html'; + if ( $fp = fopen($eventPath.'/'.$file, 'w') ) { + fwrite($fp, exportEventImages($event, $exportDetail, $exportFrames, $myfilelist)); + fclose($fp); + $exportFileList[$file] = $file; + } else { + ZM\Error("Can't open event images export file '$file'"); + } } } # end if exportImages @@ -819,17 +842,17 @@ 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; } } $files = $filesLeft; - } # end if exportVideo + } # end if exportVideo if ( $exportMisc ) { foreach ( $files as $file ) { - $exportFileList[$file] = $event->Id().'/'.$file; + $exportFileList[$file] = $file; } $files = array(); } @@ -850,10 +873,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 +886,23 @@ 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\Error("Can't create exports dir at '$export_dir'"); + return false; + } + ZM\Logger::Debug("Successfully created dir '$export_dir'"); + chmod($export_dir, 0700); + if ( !chdir($export_dir) ) { + ZM\Error("Can't chdir to $export_dir"); + return; } - if ( !chdir($export_dir) ) - ZM\Fatal("Can't chdir to $export_dir"); $export_root = 'zmExport'; $export_listFile = 'zmFileList.txt'; @@ -889,40 +912,37 @@ function exportEvents( if ( !is_array($eids) ) { $eids = array($eids); } - 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); + $exportFileList = array_merge($exportFileList, $event_exportFileList); foreach ( $event_exportFileList as $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); - } - } + #if ( preg_match('/\.html$/', $file) ) + #continue; + $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_exportFile + } # 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"); - //if ( !symlink(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/js/video.js', $export_dir.'/video.js') ) - //Error("Failed linking video.js"); + if ( !symlink(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/js/jquery.js', $export_dir.'/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"); - $html_eventMaster_file = 'zmEventImagesMaster_'.date('Ymd_His'). '.html'; - $html_eventMaster_path = $export_dir.'/'.$html_eventMaster_file; + $html_eventMaster_file = 'zmEventImagesMaster.html'; + $html_eventMaster_path = $export_dir.'/'.$html_eventMaster_file; - if ( ($fp = fopen($html_eventMaster_path, 'w')) ) { - fwrite($fp, exportEventImagesMaster($eids)); - fclose($fp); - $exportFileList[] = $html_eventMaster_file; - } else { - ZM\Error("Can't open event images export file '$html_eventMaster_path'"); - } + if ( ($fp = fopen($html_eventMaster_path, 'w')) ) { + fwrite($fp, exportEventImagesMaster($eids, $exportDetail, $exportFrames)); + fclose($fp); + $exportFileList[] = $html_eventMaster_file; + } else { + ZM\Error("Can't open event images export file '$html_eventMaster_path'"); } $listFile = $export_dir.'/'.$export_listFile; @@ -940,6 +960,8 @@ function exportEvents( $archive = ''; if ( $exportFormat == 'tar' ) { $archive = ZM_DIR_EXPORTS.'/'.$export_root.($connkey?'_'.$connkey:'').'.tar'; + $version = shell_exec('tar -v'); + $command = 'tar --create --dereference'; if ( $exportCompressed ) { $archive .= '.gz'; @@ -947,8 +969,11 @@ function exportEvents( $exportFormat .= '.gz'; } if ( $exportStructure == 'flat' ) { - //strip file paths if we - $command .= " --xform='s#^.+/##x'"; + if ( preg_match('/BSD/i', $version) ) { + $command .= ' -s \'#^.*/##\''; + } else { + $command .= ' --xform=\'s#^.+/##x\''; + } } $command .= ' --file='.escapeshellarg($archive); } elseif ( $exportFormat == 'zip' ) { @@ -960,19 +985,19 @@ 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; } - //clean up temporary files + // clean up temporary files if ( !empty($html_eventMaster) ) { unlink($monitorPath.'/'.$html_eventMaster); } return '?view=archive%26type='.$exportFormat.'%26connkey='.$connkey; -} +} // end function exportEvents diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index 67866e247..203d3ffc2 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -1,6 +1,6 @@ '; } } - return implode("\n", $html); + $html[] = ''; // So we ge a trailing \n + return implode(PHP_EOL, $html); + } + + function output_cache_busted_stylesheet_links($files) { + $html = array(); + foreach ( $files as $file ) { + $html[] = ''; + } + $html[] = ''; // So we ge a trailing \n + return implode(PHP_EOL, $html); } ?> @@ -58,10 +65,10 @@ function xhtmlHeaders($file, $title) { - - <?php echo validHtmlStr(ZM_WEB_TITLE_PREFIX); ?> - <?php echo validHtmlStr($title) ?> + + <?php echo validHtmlStr(ZM_WEB_TITLE_PREFIX) . ' - ' . validHtmlStr($title) ?> @@ -72,130 +79,52 @@ if ( file_exists( "skins/$skin/css/$css/graphics/favicon.ico" ) ) { '; } -?> - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Id().($event->Archived()?'*':'') ?> + + Id().($event->Archived()?'*':'') ?> + Name()).($event->Archived()?'*':'') ?>MonitorId(), 'zmMonitor'.$event->Monitorid(), 'monitor', $event->MonitorName(), canEdit( 'Monitors' ) ) ?>Id(), 'zmEventDetail', 'eventdetail', validHtmlStr($event->Cause()), canEdit( 'Events' ), 'title="'.htmlspecialchars($event->Notes()).'"' ) ?>MonitorId(), $event->MonitorName(), canEdit( 'Monitors' ) ) ?>Cause()), canEdit( 'Events' ), 'title="' .htmlspecialchars($event->Notes()). '" class="eDetailLink" data-eid=' .$event->Id(). '"') ?> StartTime())) . ( $event->EndTime() ? ' until ' . strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->EndTime()) ) : '' ) ?> Length() ) ?>Id(), 'zmFrames', 'frames', $event->Frames() ) ?>Id(), 'zmFrames', 'frames', $event->AlarmFrames() ) ?>Id(), $event->Frames() ) ?>Id(), $event->AlarmFrames() ) ?> TotScore() ?> AvgScore() ?>MaxScore(); - #makePopupLink('?view=frame&eid='.$event->Id().'&fid=0', 'zmImage', array('image', reScale($event->Width(), $scale), reScale($event->Height(), $scale)), $event->MaxScore()) ?>MaxScore() ?> DiskSpace()) ?>
events
events
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+ +
+

-

- - - - -
+ ?>"> + + + + + + + + +
- - + diff --git a/web/skins/classic/views/filter.php b/web/skins/classic/views/filter.php index 0c078daf0..38ad3e88c 100644 --- a/web/skins/classic/views/filter.php +++ b/web/skins/classic/views/filter.php @@ -1,6 +1,6 @@ translate('ChooseFilter')); @@ -43,14 +47,14 @@ foreach ( ZM\Filter::find(null,array('order'=>'lower(Name)')) as $Filter ) { } if ( !$filter ) { $filter = new ZM\Filter(); + + if ( isset($_REQUEST['filter']) ) { + # Update our filter object with whatever changes we have made before saving + $filter->set($_REQUEST['filter']); + } } -if ( isset($_REQUEST['filter']) ) { - # Update our filter object with whatever changes we have made before saving - #$filter->set($_REQUEST['filter']); -} - -$conjunctionTypes = getFilterQueryConjunctionTypes(); +$conjunctionTypes = ZM\getFilterQueryConjunctionTypes(); $obracketTypes = array(); $cbracketTypes = array(); @@ -68,38 +72,41 @@ if ( count($terms) ) { } $attrTypes = array( - 'MonitorId' => translate('AttrMonitorId'), - 'MonitorName' => translate('AttrMonitorName'), - 'Id' => translate('AttrId'), - 'Name' => translate('AttrName'), + 'AlarmFrames' => translate('AttrAlarmFrames'), + 'AlarmedZoneId' => translate('AttrAlarmedZone'), + 'Archived' => translate('AttrArchiveStatus'), + 'AvgScore' => translate('AttrAvgScore'), 'Cause' => translate('AttrCause'), - 'Notes' => translate('AttrNotes'), - 'StartDateTime' => translate('AttrStartDateTime'), - 'StartDate' => translate('AttrStartDate'), - 'StartTime' => translate('AttrStartTime'), - 'StartWeekday' => translate('AttrStartWeekday'), + 'DiskBlocks' => translate('AttrDiskBlocks'), + 'DiskPercent' => translate('AttrDiskPercent'), + 'DiskSpace' => translate('AttrDiskSpace'), + 'EventDiskSpace' => translate('AttrEventDiskSpace'), 'EndDateTime' => translate('AttrEndDateTime'), 'EndDate' => translate('AttrEndDate'), 'EndTime' => translate('AttrEndTime'), 'EndWeekday' => translate('AttrEndWeekday'), - 'Length' => translate('AttrDuration'), + 'ExistsInFileSystem' => translate('ExistsInFileSystem'), + 'FilterServerId' => translate('AttrFilterServer'), 'Frames' => translate('AttrFrames'), - 'AlarmFrames' => translate('AttrAlarmFrames'), - 'TotScore' => translate('AttrTotalScore'), - 'AvgScore' => translate('AttrAvgScore'), + 'Id' => translate('AttrId'), + 'Length' => translate('AttrDuration'), 'MaxScore' => translate('AttrMaxScore'), - 'Archived' => translate('AttrArchiveStatus'), - 'DiskBlocks' => translate('AttrDiskBlocks'), - 'DiskPercent' => translate('AttrDiskPercent'), - 'DiskSpace' => translate('AttrDiskSpace'), - 'SystemLoad' => translate('AttrSystemLoad'), - 'StorageId' => translate('AttrStorageArea'), + 'MonitorId' => translate('AttrMonitorId'), + 'MonitorName' => translate('AttrMonitorName'), + 'MonitorServerId' => translate('AttrMonitorServer'), + 'Name' => translate('AttrName'), + 'Notes' => translate('AttrNotes'), 'SecondaryStorageId' => translate('AttrSecondaryStorageArea'), 'ServerId' => translate('AttrMonitorServer'), - 'FilterServerId' => translate('AttrFilterServer'), - 'MonitorServerId' => translate('AttrMonitorServer'), - 'StorageServerId' => translate('AttrStorageServer'), + 'StartDateTime' => translate('AttrStartDateTime'), + 'StartDate' => translate('AttrStartDate'), + 'StartTime' => translate('AttrStartTime'), + 'StartWeekday' => translate('AttrStartWeekday'), 'StateId' => translate('AttrStateId'), + 'StorageId' => translate('AttrStorageArea'), + 'StorageServerId' => translate('AttrStorageServer'), + 'SystemLoad' => translate('AttrSystemLoad'), + 'TotScore' => translate('AttrTotalScore'), ); $opTypes = array( @@ -115,12 +122,23 @@ $opTypes = array( '![]' => translate('OpNotIn'), 'IS' => translate('OpIs'), 'IS NOT' => translate('OpIsNot'), + 'LIKE' => translate('OpLike'), + 'NOT LIKE' => translate('OpNotLike'), ); +$is_isnot_opTypes = array( + 'IS' => translate('OpIs'), + 'IS NOT' => translate('OpIsNot'), +); $archiveTypes = array( - '0' => translate('ArchUnarchived'), - '1' => translate('ArchArchived') - ); + '0' => translate('ArchUnarchived'), + '1' => translate('ArchArchived') +); + +$booleanValues = array( + 'false' => translate('False'), + 'true' => translate('True') +); $focusWindow = true; @@ -141,13 +159,24 @@ foreach ( dbFetchAll('SELECT `Id`, `Name` FROM `Servers` ORDER BY lower(`Name`) $servers[$server['Id']] = validHtmlStr($server['Name']); } $monitors = array(); +$monitor_names = array(); foreach ( dbFetchAll('SELECT `Id`, `Name` FROM `Monitors` ORDER BY lower(`Name`) ASC') as $monitor ) { if ( visibleMonitor($monitor['Id']) ) { - $monitors[$monitor['Name']] = validHtmlStr($monitor['Name']); + $monitors[$monitor['Id']] = new ZM\Monitor($monitor); + $monitor_names[] = validHtmlStr($monitor['Name']); + } +} +$zones = array(); +foreach ( dbFetchAll('SELECT Id, Name, MonitorId FROM Zones ORDER BY lower(`Name`) ASC') as $zone ) { + if ( visibleMonitor($zone['MonitorId']) ) { + if ( isset($monitors[$zone['MonitorId']]) ) { + $zone['Name'] = validHtmlStr($monitors[$zone['MonitorId']]->Name().': '.$zone['Name']); + $zones[$zone['Id']] = new ZM\Zone($zone); + } } } -xhtmlHeaders(__FILE__, translate('EventFilter') ); +xhtmlHeaders(__FILE__, translate('EventFilter')); ?>
@@ -157,7 +186,7 @@ xhtmlHeaders(__FILE__, translate('EventFilter') );
- 1 ) { echo htmlSelect('Id', $filterNames, $filter->Id(), array('data-on-change-this'=>'selectFilter')); } else { @@ -177,13 +206,25 @@ if ( (null !== $filter->Concurrent()) and $filter->Concurrent() )
- Id() ) { ?> +Id() ) { ?>

Id() ?>

- +

+ +

+ UserId() ? $filter->UserId() : $user['Id'] +); ?> +

+ +

- + + + + @@ -257,10 +300,15 @@ for ( $i=0; $i < count($terms); $i++ ) { + + + @@ -271,6 +319,11 @@ for ( $i=0; $i < count($terms); $i++ ) { ?> + + + @@ -363,8 +416,22 @@ if ( ZM_OPT_EMAIL ) { ?>

- AutoEmail() ) { ?> checked="checked" data-on-click-this="updateButtons"/> + AutoEmail() ) { ?> checked="checked" data-on-click-this="click_AutoEmail"/>

+
AutoEmail() ? '' : ' style="display:none;"' ?>> +

+ + +

+

+ + +

+

+ + +

+

- - + + +
diff --git a/web/skins/classic/views/frame.php b/web/skins/classic/views/frame.php index af412794b..dc9ef9c4f 100644 --- a/web/skins/classic/views/frame.php +++ b/web/skins/classic/views/frame.php @@ -26,16 +26,17 @@ if ( !canView('Events') ) { require_once('includes/Frame.php'); $eid = validInt($_REQUEST['eid']); -if ( !empty($_REQUEST['fid']) ) - $fid = validInt($_REQUEST['fid']); +$fid = empty($_REQUEST['fid']) ? 0 : validInt($_REQUEST['fid']); $Event = new ZM\Event($eid); $Monitor = $Event->Monitor(); +# This is kinda weird.. so if we pass fid=0 or some other non-integer, then it loads max score +# perhaps we should consider being explicit, like fid = maxscore if ( !empty($fid) ) { $sql = 'SELECT * FROM Frames WHERE EventId = ? AND FrameId = ?'; - if ( !($frame = dbFetchOne( $sql, NULL, array($eid, $fid) )) ) - $frame = array( 'EventId'=>$eid, 'FrameId'=>$fid, 'Type'=>'Normal', 'Score'=>0 ); + if ( !($frame = dbFetchOne($sql, NULL, array($eid, $fid))) ) + $frame = array('EventId'=>$eid, 'FrameId'=>$fid, 'Type'=>'Normal', 'Score'=>0); } else { $frame = dbFetchOne('SELECT * FROM Frames WHERE EventId = ? AND Score = ?', NULL, array($eid, $Event->MaxScore())); } @@ -50,19 +51,19 @@ $lastFid = $maxFid; $alarmFrame = $Frame->Type() == 'Alarm'; -if ( isset( $_REQUEST['scale'] ) ) { +if ( isset($_REQUEST['scale']) ) { $scale = validNum($_REQUEST['scale']); -} else if ( isset( $_COOKIE['zmWatchScale'.$Monitor->Id()] ) ) { +} else if ( isset($_COOKIE['zmWatchScale'.$Monitor->Id()]) ) { $scale = validNum($_COOKIE['zmWatchScale'.$Monitor->Id()]); -} else if ( isset( $_COOKIE['zmWatchScale'] ) ) { +} else if ( isset($_COOKIE['zmWatchScale']) ) { $scale = validNum($_COOKIE['zmWatchScale']); } else { - $scale = max( reScale( SCALE_BASE, $Monitor->DefaultScale(), ZM_WEB_DEFAULT_SCALE ), SCALE_BASE ); + $scale = max(reScale(SCALE_BASE, $Monitor->DefaultScale(), ZM_WEB_DEFAULT_SCALE), SCALE_BASE); } -$scale = $scale ?: 'auto'; +$scale = $scale ? $scale : 0; -$imageData = $Event->getImageSrc( $frame, $scale, 0 ); -if ( ! $imageData ) { +$imageData = $Event->getImageSrc($frame, $scale, 0); +if ( !$imageData ) { ZM\Error("No data found for Event $eid frame $fid"); $imageData = array(); } @@ -84,32 +85,41 @@ $focusWindow = true; xhtmlHeaders(__FILE__, translate('Frame').' - '.$Event->Id().' - '.$Frame->FrameId()); ?> -
-
2 ) { echo htmlSelect("filter[Query][terms][$i][obr]", $obracketTypes, $term['obr']); } else { ?> 'checkValue')); ?> - - -
- - - - - - - - - - - - - - - + + +
+
+ + + + + + + + + + - + - - - + + + - - - - - + + + + + + + - - - - - -Id(); + $thmb_width = ZM_WEB_LIST_THUMB_WIDTH ? 'width='.ZM_WEB_LIST_THUMB_WIDTH : ''; + $thmb_height = ZM_WEB_LIST_THUMB_HEIGHT ? 'height='.ZM_WEB_LIST_THUMB_HEIGHT : ''; + $thmb_fn = 'filename=' .$Event->MonitorId(). '_' .$frame['EventId']. '_' .$frame['FrameId']. '.jpg'; + $img_src = join('&', array_filter(array($base_img_src, $thmb_width, $thmb_height, $thmb_fn))); + $full_img_src = join('&', array_filter(array($base_img_src, $thmb_fn))); + $frame_src = '?view=frame&eid=' .$Event->Id(). '&fid=' .$frame['FrameId']; + + echo ''.PHP_EOL; + } ?>
Id().'&fid='.$frame['FrameId'], 'zmImage', - array( - 'frame', - ($scale ? $Event->Width()*$scale/100 : $Event->Width()), - ($scale ? $Event->Height()*$scale/100 : $Event->Height()) - ), - $frame['FrameId']) - ?>
Id().'&fid='.$frame['FrameId'], 'zmStats', 'stats', $frame['Score']) ?>Id().'&fid='.$frame['FrameId'], 'zmImage', array('image', $Event->Width(), $Event->Height()), ''.$frame['FrameId'].'' ) ?>
- - -

- - -
-
- -
+
- - + diff --git a/web/skins/classic/views/function.php b/web/skins/classic/views/function.php deleted file mode 100644 index 1cd2985ef..000000000 --- a/web/skins/classic/views/function.php +++ /dev/null @@ -1,63 +0,0 @@ -$_REQUEST['mid'])); - -$focusWindow = true; - -xhtmlHeaders(__FILE__, translate('Function').' - '.validHtmlStr($monitor->Name())); -?> - -
- -
-
- - - -

- - - Enabled()) ) { ?> checked="checked"/> -

-
- - -
-
-
-
- - diff --git a/web/skins/classic/views/group.php b/web/skins/classic/views/group.php index aa8ec4b56..4b9ca6152 100644 --- a/web/skins/classic/views/group.php +++ b/web/skins/classic/views/group.php @@ -37,14 +37,14 @@ xhtmlHeaders(__FILE__, translate('Group').' - '.$newGroup->Name());

- Name()); ?>

-
+ - + @@ -88,19 +88,19 @@ function get_children($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'; +$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']; } -echo htmlSelect('newGroup[ParentId]', $options, $newGroup->ParentId(), array('onchange'=>'configureButtons(this);')); +echo htmlSelect('newGroup[ParentId]', $options, $newGroup->ParentId(), array('data-on-change'=>'configureButtons')); ?> + + @@ -67,14 +69,15 @@ function group_line( $Group ) { $html .= str_repeat('', $Group->depth()); $html .= ' - - - '; + $html .= ''; + if ( canEdit('Groups') ) { + $html .= ''; + } + $html .= ''; if ( isset( $children[$Group->Id()] ) ) { foreach ( $children[$Group->Id()] as $G ) { $html .= group_line($G); @@ -99,5 +102,4 @@ if ( isset( $children[null] ) ) - - + diff --git a/web/skins/classic/views/image-ffmpeg.php b/web/skins/classic/views/image-ffmpeg.php deleted file mode 100644 index 496b4c695..000000000 --- a/web/skins/classic/views/image-ffmpeg.php +++ /dev/null @@ -1,76 +0,0 @@ -$fid, 'Type'=>'Normal', 'Score'=>0 ); -} else { - $frame = dbFetchOne( 'SELECT * FROM Frames WHERE EventId = ? AND Score = ?', NULL, array( $eid, $event['MaxScore'] ) ); -} - -$maxFid = $event['Frames']; - -$firstFid = 1; -$prevFid = $frame['FrameId']-1; -$nextFid = $frame['FrameId']+1; -$lastFid = $maxFid; - -$alarmFrame = $frame['Type']=='Alarm'; - -if ( isset( $_REQUEST['scale'] ) ) - $scale = validInt($_REQUEST['scale']); -else - $scale = max( reScale( SCALE_BASE, $event['DefaultScale'], ZM_WEB_DEFAULT_SCALE ), SCALE_BASE ); - -$Transpose = ''; -if ( $event['VideoWriter'] == "2" ) { // PASSTHROUGH - $Rotation = $event['Orientation']; -// rotate right - if ( in_array($event['Orientation'],array("90"))) - $Transpose = 'transpose=1,'; -// rotate 180 // upside down cam - if ( in_array($event['Orientation'],array("180"))) - $Transpose = 'transpose=2,transpose=2,'; -// rotate left - if ( in_array($event['Orientation'],array("270"))) - $Transpose = 'transpose=2,'; -} -$focusWindow = true; -$Scale = 100/$scale; -$fid = $fid - 1; -#$command = 'ffmpeg -v 0 -i '.getEventDefaultVideoPath($event).' -vf "select=gte(selected_n\,'.$fid.'),setpts=PTS-STARTPTS" '.$Transpose.',scale=iw/'.$Scale.':-1" -frames:v 1 -f mjpeg -'; -$command = 'ffmpeg -v 0 -i '.getEventDefaultVideoPath($event).' -vf "select=gte(n\\,'.$fid.'),setpts=PTS-STARTPTS,'.$Transpose.'scale=iw/'.$Scale.':-1" -f image2 -'; -header('Content-Type: image/jpeg'); -system($command); -?> diff --git a/web/skins/classic/views/js/console.js b/web/skins/classic/views/js/console.js index 469a54331..47201689d 100644 --- a/web/skins/classic/views/js/console.js +++ b/web/skins/classic/views/js/console.js @@ -1,11 +1,28 @@ +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'); +} + +function initThumbAnimation() { + $j('.colThumbnail img').each(function() { + this.addEventListener('mouseover', thumbnail_onmouseover, false); + this.addEventListener('mouseout', thumbnail_onmouseout, false); + }); +} function setButtonStates( element ) { var form = element.form; var checked = 0; for ( var i=0; i < form.elements.length; i++ ) { if ( - form.elements[i].type=="checkbox" - && + form.elements[i].type=="checkbox" && form.elements[i].name=="markMids[]" ) { var tr = $j(form.elements[i]).closest("tr"); @@ -35,7 +52,7 @@ function setButtonStates( element ) { } function addMonitor(element) { - createPopup( '?view=monitor', 'zmMonitor0', 'monitor' ); + window.location.assign('?view=monitor'); } function cloneMonitor(element) { @@ -44,10 +61,8 @@ function cloneMonitor(element) { // get the value of the first checkbox for ( var i = 0; i < form.elements.length; i++ ) { if ( - form.elements[i].type == "checkbox" - && - form.elements[i].name == "markMids[]" - && + form.elements[i].type == "checkbox" && + form.elements[i].name == "markMids[]" && form.elements[i].checked ) { monitorId = form.elements[i].value; @@ -55,7 +70,7 @@ function cloneMonitor(element) { } } // end foreach element if ( monitorId != -1 ) { - createPopup( '?view=monitor&dupId='+monitorId, 'zmMonitor0', 'monitor' ); + window.location.assign('?view=monitor&dupId='+monitorId); } } @@ -65,10 +80,8 @@ function editMonitor( element ) { for ( var i = 0; i < form.elements.length; i++ ) { if ( - form.elements[i].type == "checkbox" - && - form.elements[i].name == "markMids[]" - && + form.elements[i].type == "checkbox" && + form.elements[i].name == "markMids[]" && form.elements[i].checked ) { monitorIds.push( form.elements[i].value ); @@ -79,11 +92,11 @@ function editMonitor( element ) { } } // end foreach checkboxes if ( monitorIds.length == 1 ) { - createPopup( '?view=monitor&mid='+monitorIds[0], 'zmMonitor'+monitorIds[0], 'monitor' ); + window.location.assign('?view=monitor&mid='+monitorIds[0]); } else if ( monitorIds.length > 1 ) { - createPopup( '?view=monitors&'+(monitorIds.map(function(mid) { + window.location.assign( '?view=monitors&'+(monitorIds.map(function(mid) { return 'mids[]='+mid; - }).join('&')), 'zmMonitors', 'monitors' ); + }).join('&'))); } } @@ -100,13 +113,11 @@ function selectMonitor(element) { var url = thisUrl+'?view=console'; for ( var i = 0; i < form.elements.length; i++ ) { if ( - form.elements[i].type == "checkbox" - && - form.elements[i].name == "markMids[]" - && - form.elements[i].checked + form.elements[i].type == 'checkbox' && + form.elements[i].name == 'markMids[]' && + form.elements[i].checked ) { - url += '&MonitorId='+form.elements[i].value; + url += '&MonitorId[]='+form.elements[i].value; } } window.location.replace(url); @@ -116,23 +127,94 @@ function reloadWindow() { window.location.replace( thisUrl ); } +// Manage the the Function modal and its buttons +function manageFunctionModal() { + $j('.functionLnk').click(function(evt) { + evt.preventDefault(); + if ( !canEditEvents ) { + enoperm(); + return; + } + var mid = evt.currentTarget.getAttribute('data-mid'); + monitor = monitors[mid]; + if ( !monitor ) { + console.error("No monitor found for mid " + mid); + return; + } + + var function_form = document.getElementById('function_form'); + if ( !function_form ) { + console.error("Unable to find form with id function_form"); + return; + } + function_form.elements['newFunction'].value = monitor.Function; + function_form.elements['newEnabled'].checked = monitor.Enabled == '1'; + function_form.elements['mid'].value = mid; + document.getElementById('function_monitor_name').innerHTML = monitor.Name; + + $j('#modalFunction').modal('show'); + }); + + // Manage the CANCEL modal buttons + $j('.funcCancelBtn').click(function(evt) { + evt.preventDefault(); + $j('#modalFunction').modal('hide'); + }); + + // Manage the SAVE modal buttons + $j('.funcSaveBtn').click(function(evt) { + evt.preventDefault(); + $j('#function_form').submit(); + }); +} + function initPage() { - reloadWindow.periodical( consoleRefreshTimeout ); + reloadWindow.periodical(consoleRefreshTimeout); if ( showVersionPopup ) { - createPopup( '?view=version', 'zmVersion', 'version' ); + window.location.assign('?view=version'); } if ( showDonatePopup ) { - createPopup( '?view=donate', 'zmDonate', 'donate' ); + $j.getJSON(thisUrl + '?request=modal&modal=donate') + .done(function(data) { + if ( $j('#donate').length ) { + $j('#donate').replaceWith(data.html); + } else { + $j("body").append(data.html); + } + $j('#donate').modal('show'); + // Manage the Apply button + $j('#donateApplyBtn').click(function(evt) { + evt.preventDefault(); + $j('#donateForm').submit(); + }); + }) + .fail(logAjaxFail); } // Makes table sortable $j( function() { $j( "#consoleTableBody" ).sortable({ - handle: ".glyphicon-sort", + handle: ".sort", update: applySort, axis: 'Y'} ); $j( "#consoleTableBody" ).disableSelection(); } ); + + // Setup the thumbnail video animation + initThumbAnimation(); + + // Load the Function modal on page load + $j.getJSON(thisUrl + '?request=modal&modal=function') + .done(function(data) { + if ( $j('#modalFunction').length ) { + $j('#modalFunction').replaceWith(data.html); + } else { + $j("body").append(data.html); + } + // Manage the Function modal + manageFunctionModal(); + }) + .fail(logAjaxFail); } function applySort(event, ui) { diff --git a/web/skins/classic/views/js/console.js.php b/web/skins/classic/views/js/console.js.php index f5e65332d..6ef6c199c 100644 --- a/web/skins/classic/views/js/console.js.php +++ b/web/skins/classic/views/js/console.js.php @@ -1,20 +1,42 @@ var consoleRefreshTimeout = ; 0 ) { if ( ZM_DYN_DONATE_REMINDER_TIME < time() ) { $showDonatePopup = true; } } else { $nextReminder = time() + 30*24*60*60; - dbQuery( "update Config set Value = '".$nextReminder."' where Name = 'ZM_DYN_DONATE_REMINDER_TIME'" ); + dbQuery("UPDATE Config SET Value = '".$nextReminder."' WHERE Name = 'ZM_DYN_DONATE_REMINDER_TIME'"); } } -} +} // end if canEdit('System') ?> var showVersionPopup = ; var showDonatePopup = ; +var monitors = new Array(); + + monitors[Id() ?>] = { + 'Id': Id() ?>, + 'Name': 'Name() ?>', + 'ViewWidth': ViewWidth() ?>, + 'ViewHeight':ViewHeight() ?>, + 'Url': 'UrlToIndex( ZM_MIN_STREAMING_PORT ? ($monitor->Id() + ZM_MIN_STREAMING_PORT) : '') ?>', + 'Type': 'Type() ?>', + 'Function': 'Function() ?>', + 'Enabled': 'Enabled() ?>' +}; + diff --git a/web/skins/classic/views/js/controlcap.js b/web/skins/classic/views/js/controlcap.js index c52ab3dd5..ff21286c2 100644 --- a/web/skins/classic/views/js/controlcap.js +++ b/web/skins/classic/views/js/controlcap.js @@ -3,21 +3,108 @@ 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/controlpreset.js.php b/web/skins/classic/views/js/controlpreset.js.php index d358b3369..9bd8155f1 100644 --- a/web/skins/classic/views/js/controlpreset.js.php +++ b/web/skins/classic/views/js/controlpreset.js.php @@ -1,3 +1,6 @@ + var labels = new Array(); $label ) { diff --git a/web/skins/classic/views/js/cycle.js b/web/skins/classic/views/js/cycle.js index 4e6de5320..6d2c3bdd4 100644 --- a/web/skins/classic/views/js/cycle.js +++ b/web/skins/classic/views/js/cycle.js @@ -9,24 +9,27 @@ function cyclePause() { $('pauseBtn').disabled = true; $('playBtn').disabled = false; } + function cycleStart() { periodical_id = nextCycleView.periodical(cycleRefreshTimeout); $('pauseBtn').disabled = false; $('playBtn').disabled = true; } + function cycleNext() { monIdx ++; if ( monIdx >= monitorData.length ) { monIdx = 0; } if ( !monitorData[monIdx] ) { - console.log("No monitorData for " + monIdx); + console.log('No monitorData for ' + monIdx); } window.location.replace('?view=cycle&mid='+monitorData[monIdx].id+'&mode='+mode, cycleRefreshTimeout); } + function cyclePrev() { - if (monIdx) { + if ( monIdx ) { monIdx -= 1; } else { monIdx = monitorData.length - 1; @@ -46,7 +49,7 @@ function changeSize() { // Scale the frame monitor_frame = $j('#imageFeed'); if ( !monitor_frame ) { - console.log("Error finding frame"); + console.log('Error finding frame'); return; } if ( width ) { @@ -71,7 +74,7 @@ function changeSize() { streamImg.style.width = width ? width : null; streamImg.style.height = height ? height : null; } else { - console.log("Did not find liveStream"+monitorData[monIdx].id); + console.log('Did not find liveStream'+monitorData[monIdx].id); } $('scale').set('value', ''); Cookie.write('zmCycleScale', '', {duration: 10*365}); @@ -92,32 +95,51 @@ function changeScale() { // Scale the frame monitor_frame = $j('#imageFeed'); if ( !monitor_frame ) { - console.log("Error finding frame"); + console.log('Error finding frame'); return; } - if ( newWidth ) { - monitor_frame.css('width', newWidth+'px'); - } - if ( newHeight ) { - monitor_frame.css('height', newHeight+'px'); + + if ( scale != '0' && scale != '' && scale != 'auto' ) { + if ( newWidth ) { + monitor_frame.css('width', newWidth+'px'); + } + if ( newHeight ) { + monitor_frame.css('height', newHeight+'px'); + } + } else { + monitor_frame.css('width', '100%'); + monitor_frame.css('height', 'auto'); } + /*Stream could be an applet so can't use moo tools*/ var streamImg = $j('#liveStream'+monitorData[monIdx].id)[0]; - if ( streamImg ) { - if ( streamImg.nodeName == 'IMG' ) { - var src = streamImg.src; - streamImg.src = ''; + if ( !streamImg ) { + console.log("Did not find liveStream"+monitorData[monIdx].id); + return; + } - //src = src.replace(/rand=\d+/i,'rand='+Math.floor((Math.random() * 1000000) )); - src = src.replace(/scale=[\.\d]+/i, 'scale='+scale); + if ( streamImg.nodeName == 'IMG' ) { + var src = streamImg.src; + streamImg.src = ''; + + //src = src.replace(/rand=\d+/i,'rand='+Math.floor((Math.random() * 1000000) )); + src = src.replace(/scale=[\.\d]+/i, 'scale='+scale); + if ( scale != '0' && scale != '' && scale != 'auto' ) { src = src.replace(/width=[\.\d]+/i, 'width='+newWidth); src = src.replace(/height=[\.\d]+/i, 'height='+newHeight); - streamImg.src = src; + } else { + src = src.replace(/width=[\.\d]+/i, 'width='+monitorData[monIdx].width); + src = src.replace(/height=[\.\d]+/i, 'height='+monitorData[monIdx].height); } - streamImg.style.width = newWidth + 'px'; - streamImg.style.height = newHeight + 'px'; + streamImg.src = src; + } + + if ( scale != '0' && scale != '' && scale != 'auto' ) { + streamImg.style.width = newWidth+'px'; + streamImg.style.height = newHeight+'px'; } else { - console.log("Did not find liveStream"+monitorData[monIdx].id); + streamImg.style.width = '100%'; + streamImg.style.height = 'auto'; } } // end function changeScale() diff --git a/web/skins/classic/views/js/cycle.js.php b/web/skins/classic/views/js/cycle.js.php index 316296410..f53ab006d 100644 --- a/web/skins/classic/views/js/cycle.js.php +++ b/web/skins/classic/views/js/cycle.js.php @@ -1,3 +1,9 @@ + var monIdx = ''; var nextMid = ""; var mode = ""; @@ -9,11 +15,10 @@ foreach ( $monitors as $monitor ) { ?> monitorData[monitorData.length] = { 'id': Id() ?>, - 'connKey': connKey() ?>, - 'width': Width() ?>, - 'height':Height() ?>, + 'width': ViewWidth() ?>, + 'height':ViewHeight() ?>, 'url': 'UrlToIndex() ?>', - 'onclick': function(){createPopup( '?view=watch&mid=Id() ?>', 'zmWatchId() ?>', 'watch', Width(), $monitor->PopupScale() ); ?>, Height(), $monitor->PopupScale() ); ?> );}, + '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/donate.js b/web/skins/classic/views/js/donate.js deleted file mode 100644 index 0cfb42fae..000000000 --- a/web/skins/classic/views/js/donate.js +++ /dev/null @@ -1,3 +0,0 @@ -if ( action == "donate" && option == "go" ) { - zmWindow(); -} diff --git a/web/skins/classic/views/js/donate.js.php b/web/skins/classic/views/js/donate.js.php deleted file mode 100644 index 4fc36d5bb..000000000 --- a/web/skins/classic/views/js/donate.js.php +++ /dev/null @@ -1,2 +0,0 @@ -var action = ''; -var option = ''; diff --git a/web/skins/classic/views/js/download.js b/web/skins/classic/views/js/download.js index 717ab76de..16b47c8b4 100644 --- a/web/skins/classic/views/js/download.js +++ b/web/skins/classic/views/js/download.js @@ -29,11 +29,11 @@ function exportProgress() { function exportResponse(respObj, respText) { console.log(respObj); window.location.replace( - thisUrl+'?view='+currentView+'&'+eidParm - +'&exportFormat='+respObj.exportFormat - +'&exportFile='+respObj.exportFile - +'&generated='+((respObj.result=='Ok')?1:0) - +'&connkey='+connkey + thisUrl+'?view='+currentView+'&'+eidParm+ + '&exportFormat='+respObj.exportFormat+ + '&exportFile='+respObj.exportFile+ + '&generated='+((respObj.result=='Ok')?1:0)+ + '&connkey='+connkey ); } diff --git a/web/skins/classic/views/js/download.js.php b/web/skins/classic/views/js/download.js.php index 0d0c05679..390c9d17f 100644 --- a/web/skins/classic/views/js/download.js.php +++ b/web/skins/classic/views/js/download.js.php @@ -1,16 +1,19 @@ var eidParm = ''; var eidParm = 'eid='; var exportReady = ; diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index 79893336f..e20d460cf 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -1,4 +1,7 @@ var vid = null; +var spf = Math.round((eventData.Length / eventData.Frames)*1000000 )/1000000;//Seconds per frame for videojs frame by frame. +var intervalRewind; +var revSpeed = .5; // Function called when video.js hits the end of the video function vjsReplay() { @@ -39,14 +42,15 @@ function vjsReplay() { streamNext(true); break; } -} +} // end function vjsReplay $j.ajaxSetup({timeout: AJAX_TIMEOUT}); //sets timeout for all getJSON. var cueFrames = null; //make cueFrames available even if we don't send another ajax query function initialAlarmCues(eventId) { - $j.getJSON(thisUrl + '?view=request&request=status&entity=frames&id=' + eventId, setAlarmCues); //get frames data for alarmCues and inserts into html + $j.getJSON(thisUrl + '?view=request&request=status&entity=frames&id=' + eventId, setAlarmCues) //get frames data for alarmCues and inserts into html + .fail(logAjaxFail); } function setAlarmCues(data) { @@ -57,7 +61,7 @@ function setAlarmCues(data) { function renderAlarmCues(containerEl) { if ( !( cueFrames && cueFrames.length ) ) { - console.log("No cue frames for event"); + console.log('No cue frames for event'); return; } // This uses the Delta of the last frame to get the length of the event. I can't help but wonder though @@ -67,14 +71,14 @@ function renderAlarmCues(containerEl) { var spanTimeStart = 0; var spanTimeEnd = 0; var alarmed = 0; - var alarmHtml = ""; + var alarmHtml = ''; var pixSkew = 0; var skip = 0; var num_cueFrames = cueFrames.length; for ( var i = 0; i < num_cueFrames; i++ ) { skip = 0; frame = cueFrames[i]; - if (frame.Type == "Alarm" && alarmed == 0) { //From nothing to alarm. End nothing and start alarm. + if ( (frame.Type == 'Alarm') && (alarmed == 0) ) { //From nothing to alarm. End nothing and start alarm. alarmed = 1; if (frame.Delta == 0) continue; //If event starts with an alarm or too few for a nonespan spanTimeEnd = frame.Delta * 100; @@ -88,24 +92,24 @@ function renderAlarmCues(containerEl) { } alarmHtml += ''; spanTimeStart = spanTimeEnd; - } else if (frame.Type !== "Alarm" && alarmed == 1) { //from alarm to nothing. End alarm and start nothing. + } else if ( (frame.Type !== 'Alarm') && (alarmed == 1) ) { //from alarm to nothing. End alarm and start nothing. futNone = 0; indexPlus = i+1; if (((frame.Delta * 100) - spanTimeStart) < minAlarm && indexPlus < num_cueFrames) { //alarm is too short and there is more event continue; } - while (futNone < minAlarm) { //check ahead to see if there's enough for a nonespan - if (indexPlus >= cueFrames.length) break; //check if end of event. + while ( futNone < minAlarm ) { //check ahead to see if there's enough for a nonespan + if ( indexPlus >= cueFrames.length ) break; //check if end of event. futNone = (cueFrames[indexPlus].Delta *100) - (frame.Delta *100); - if (cueFrames[indexPlus].Type == "Alarm") { + if ( cueFrames[indexPlus].Type == 'Alarm' ) { i = --indexPlus; skip = 1; break; } indexPlus++; } - if (skip == 1) continue; //javascript doesn't support continue 2; + if ( skip == 1 ) continue; //javascript doesn't support continue 2; spanTimeEnd = frame.Delta *100; spanTime = spanTimeEnd - spanTimeStart; alarmed = 0; @@ -118,7 +122,7 @@ function renderAlarmCues(containerEl) { } alarmHtml += ''; spanTimeStart = spanTimeEnd; - } else if (frame.Type == "Alarm" && alarmed == 1 && i + 1 >= cueFrames.length) { //event ends on an alarm + } else if ( (frame.Type == 'Alarm') && (alarmed == 1) && (i + 1 >= cueFrames.length) ) { //event ends on an alarm spanTimeEnd = frame.Delta * 100; spanTime = spanTimeEnd - spanTimeStart; alarmed = 0; @@ -130,21 +134,9 @@ function renderAlarmCues(containerEl) { return alarmHtml; } -function setButtonState( element, butClass ) { - if ( element ) { - element.className = butClass; - if (butClass == 'unavail' || (butClass == 'active' && (element.id == 'pauseBtn' || element.id == 'playBtn'))) { - element.disabled = true; - } else { - element.disabled = false; - } - } else { - console.log("Element was null in setButtonState"); - } -} -function changeCodec(element) { - location.replace(thisUrl + '?view=event&eid=' + eventData.Id + filterQuery + sortQuery+'&codec='+element.value); +function changeCodec() { + location.replace(thisUrl + '?view=event&eid=' + eventData.Id + filterQuery + sortQuery+'&codec='+$j('#codec').val()); } function changeScale() { @@ -155,12 +147,12 @@ function changeScale() { var eventViewer; var alarmCue = $j('div.alarmCue'); var bottomEl = streamMode == 'stills' ? $j('#eventImageNav') : $j('#replayStatus'); - if (streamMode == 'stills') { + if ( streamMode == 'stills' ) { eventViewer = $j('#eventThumbs'); } else { eventViewer = $j(vid ? '#videoobj' : '#evtStream'); } - if ( scale == "auto" ) { + if ( scale == '0' || scale == 'auto' ) { var newSize = scaleToFit(eventData.Width, eventData.Height, eventViewer, bottomEl); newWidth = newSize.width; newHeight = newSize.height; @@ -170,12 +162,12 @@ function changeScale() { newWidth = eventData.Width * scale / SCALE_BASE; newHeight = eventData.Height * scale / SCALE_BASE; } - if ( !(streamMode == 'stills') ) { + if ( streamMode != 'stills' ) { eventViewer.width(newWidth); - } //stills handles its own width + } // stills handles its own width eventViewer.height(newHeight); if ( !vid ) { // zms needs extra sizing - streamScale(scale == "auto" ? autoScale : scale); + streamScale(scale == '0' ? autoScale : scale); drawProgressBar(); } if ( streamMode == 'stills' ) { @@ -184,13 +176,13 @@ function changeScale() { } else { alarmCue.html(renderAlarmCues(eventViewer));//just re-render alarmCues. skip ajax call } - if ( scale == "auto" ) { + if ( scale == '0' ) { Cookie.write('zmEventScaleAuto', 'auto', {duration: 10*365}); } else { Cookie.write('zmEventScale'+eventData.MonitorId, scale, {duration: 10*365}); Cookie.dispose('zmEventScaleAuto'); } -} +} // end function changeScale function changeReplayMode() { var replayMode = $('replayMode').get('value'); @@ -200,7 +192,36 @@ function changeReplayMode() { refreshWindow(); } +function changeRate() { + var rate = parseInt($j('select[name="rate"]').val()); + if ( ! rate ) { + pauseClicked(); + } else if ( rate < 0 ) { + if ( vid ) { //There is no reverse play with mp4. Set the speed to 0 and manually set the time back. + revSpeed = rates[rates.indexOf(-1*rate)-1]/100; + clearInterval(intervalRewind); + intervalRewind = setInterval(function() { + if ( vid.currentTime() <= 0 ) { + clearInterval(intervalRewind); + vid.pause(); + } else { + vid.playbackRate(0); + vid.currentTime(vid.currentTime() - (revSpeed/2)); //Half of reverse speed because our interval is 500ms. + } + }, 500); //500ms is a compromise between smooth reverse and realistic performance + } // end if vid + } else { // Forward rate + if ( vid ) { + vid.playbackRate(rate/100); + } + } + Cookie.write('zmEventRate', rate, {duration: 10*365}); +} // end function changeRate + var streamParms = "view=request&request=stream&connkey="+connKey; +if ( auth_hash ) { + streamParms += '&auth='+auth_hash; +} var streamCmdTimer = null; var streamStatus = null; @@ -208,7 +229,7 @@ var lastEventId = 0; var zmsBroke = false; //Use alternate navigation if zms has crashed function getCmdResponse( respObj, respText ) { - if ( checkStreamForErrors("getCmdResponse", respObj) ) { + if ( checkStreamForErrors('getCmdResponse', respObj) ) { console.log('Got an error from getCmdResponse'); console.log(respObj); console.log(respText); @@ -242,7 +263,8 @@ function getCmdResponse( respObj, respText ) { if ( streamStatus.paused == true ) { streamPause( ); } else { - $j('#rateValue').html(streamStatus.rate); + console.log('streamStatus.rate: ' + streamStatus.rate); + $j('select[name="rate"]').val(streamStatus.rate*100); Cookie.write('zmEventRate', streamStatus.rate*100, {duration: 10*365}); streamPlay( ); } @@ -264,11 +286,11 @@ function getCmdResponse( respObj, respText ) { } } // end if haev a new auth hash - streamCmdTimer = streamQuery.delay( streamTimeout ); //Timeout is refresh rate for progressBox and time display + streamCmdTimer = streamQuery.delay(streamTimeout); //Timeout is refresh rate for progressBox and time display } var streamReq = new Request.JSON( { - url: thisUrl, + url: monitorUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'chain', @@ -277,23 +299,18 @@ var streamReq = new Request.JSON( { function pauseClicked() { if ( vid ) { + if ( intervalRewind ) { + stopFastRev(); + } vid.pause(); } else { streamReq.send(streamParms+"&command="+CMD_PAUSE); - streamPause(); - } -} - -function vjsPause() { - if ( intervalRewind ) { - stopFastRev(); } streamPause(); } -function streamPause( ) { +function streamPause() { $j('#modeValue').html('Paused'); - $j('#rateValue').html('0'); setButtonState( $('pauseBtn'), 'active' ); setButtonState( $('playBtn'), 'inactive' ); setButtonState( $('fastFwdBtn'), 'unavail' ); @@ -303,6 +320,10 @@ function streamPause( ) { } function playClicked( ) { + var rate_select = $j('select[name="rate"]'); + if ( ! rate_select.val() ) { + $j('select[name="rate"]').val(100); + } if ( vid ) { if ( vid.paused() ) { vid.play(); @@ -310,22 +331,21 @@ function playClicked( ) { vjsPlay(); //handles fast forward and rewind } } else { - streamReq.send( streamParms+"&command="+CMD_PLAY ); - streamPlay(); + streamReq.send(streamParms+"&command="+CMD_PLAY); } + streamPlay(); } function vjsPlay() { //catches if we change mode programatically if ( intervalRewind ) { stopFastRev(); } - $j('#rateValue').html(vid.playbackRate()); + $j('select[name="rate"]').val(vid.playbackRate()*100); Cookie.write('zmEventRate', vid.playbackRate()*100, {duration: 10*365}); streamPlay(); } function streamPlay( ) { - $j('#modeValue').html('Replay'); setButtonState( $('pauseBtn'), 'inactive' ); setButtonState( $('playBtn'), 'active' ); setButtonState( $('fastFwdBtn'), 'inactive' ); @@ -347,16 +367,13 @@ function streamFastFwd( action ) { if ( rates.indexOf(vid.playbackRate()*100)-1 == -1 ) { setButtonState($('fastFwdBtn'), 'unavail'); } - $j('#rateValue').html(vid.playbackRate()); + $j('select[name="rate"]').val(vid.playbackRate()*100); Cookie.write('zmEventRate', vid.playbackRate()*100, {duration: 10*365}); } else { streamReq.send(streamParms+"&command="+CMD_FASTFWD); } } -var spf = Math.round((eventData.Length / eventData.Frames)*1000000 )/1000000;//Seconds per frame for videojs frame by frame. -var intervalRewind; -var revSpeed = .5; function streamSlowFwd( action ) { if ( vid ) { @@ -377,6 +394,7 @@ function streamSlowRev( action ) { function stopFastRev() { clearInterval(intervalRewind); vid.playbackRate(1); + $j('select[name="rate"]').val(vid.playbackRate()*100); Cookie.write('zmEventRate', vid.playbackRate()*100, {duration: 10*365}); revSpeed = .5; } @@ -394,7 +412,7 @@ function streamFastRev( action ) { setButtonState( $('fastRevBtn'), 'unavail' ); } clearInterval(intervalRewind); - $j('#rateValue').html(-revSpeed); + $j('select[name="rate"]').val(-revSpeed*100); Cookie.write('zmEventRate', vid.playbackRate()*100, {duration: 10*365}); intervalRewind = setInterval(function() { if (vid.currentTime() <= 0) { @@ -406,7 +424,7 @@ function streamFastRev( action ) { } }, 500); //500ms is a compromise between smooth reverse and realistic performance } else { - streamReq.send( streamParms+"&command="+CMD_FASTREV ); + streamReq.send(streamParms+"&command="+CMD_FASTREV); } } @@ -538,9 +556,9 @@ var scroll = null; var currEventId = null; var CurEventDefVideoPath = null; -function getEventResponse( respObj, respText ) { - if ( checkStreamForErrors( "getEventResponse", respObj ) ) { - console.log("getEventResponse: errors" ); +function getEventResponse(respObj, respText) { + if ( checkStreamForErrors('getEventResponse', respObj) ) { + console.log('getEventResponse: errors'); return; } @@ -579,13 +597,13 @@ function getEventResponse( respObj, respText ) { //eventImg.setStyles( { 'width': eventData.width, 'height': eventData.height } ); if ( vid && CurEventDefVideoPath ) { vid.src({type: 'video/mp4', src: CurEventDefVideoPath}); //Currently mp4 is all we use - console.log("getEventResponse"); + console.log('getEventResponse'); initialAlarmCues(eventData.Id);//ajax and render, new event addVideoTimingTrack(vid, LabelFormat, eventData.MonitorName, eventData.Length, eventData.StartTime); CurEventDefVideoPath = null; $j('#modeValue').html('Replay'); $j('#zoomValue').html('1'); - $j('#rateValue').html('1'); + $j('#rate').val('100'); vjsPanZoom('zoomOut'); } else { drawProgressBar(); @@ -597,6 +615,9 @@ var eventReq = new Request.JSON( {url: thisUrl, method: 'get', timeout: AJAX_TIM function eventQuery( eventId ) { var eventParms = "view=request&request=status&entity=event&id="+eventId; + if ( auth_hash ) { + eventParms += '&auth='+auth_hash; + } eventReq.send( eventParms ); } @@ -638,7 +659,7 @@ var frameBatch = 40; function loadEventThumb( event, frame, loadImage ) { var thumbImg = $('eventThumb'+frame.FrameId); if ( !thumbImg ) { - console.error( "No holder found for frame "+frame.FrameId ); + console.error('No holder found for frame '+frame.FrameId); return; } var img = new Asset.image( imagePrefix+frame.EventId+"&fid="+frame.FrameId, @@ -660,31 +681,31 @@ function loadEventThumb( event, frame, loadImage ) { ); } -function loadEventImage( event, frame ) { - console.debug( "Loading "+event.Id+"/"+frame.FrameId ); +function loadEventImage(event, frame) { + console.debug('Loading '+event.Id+'/'+frame.FrameId); var eventImg = $('eventImage'); var thumbImg = $('eventThumb'+frame.FrameId); - if ( eventImg.getProperty( 'src' ) != thumbImg.getProperty( 'src' ) ) { + if ( eventImg.getProperty('src') != thumbImg.getProperty('src') ) { var eventImagePanel = $('eventImagePanel'); - if ( eventImagePanel.getStyle( 'display' ) != 'none' ) { - var lastThumbImg = $('eventThumb'+eventImg.getProperty( 'alt' )); + if ( eventImagePanel.getStyle('display') != 'none' ) { + var lastThumbImg = $('eventThumb'+eventImg.getProperty('alt')); lastThumbImg.removeClass('selected'); - lastThumbImg.setOpacity( 1.0 ); + lastThumbImg.setOpacity(1.0); } - $('eventImageBar').setStyle( 'width', event.Width ); - if ( frame.Type=='Alarm' ) { - $('eventImageStats').removeClass( 'hidden' ); + $('eventImageBar').setStyle('width', event.Width); + if ( frame.Type == 'Alarm' ) { + $('eventImageStats').removeClass('hidden'); } else { - $('eventImageStats').addClass( 'hidden' ); + $('eventImageStats').addClass('hidden'); } - thumbImg.addClass( 'selected' ); - thumbImg.setOpacity( 0.5 ); + thumbImg.addClass('selected'); + thumbImg.setOpacity(0.5); - if ( eventImagePanel.getStyle( 'display' ) == 'none' ) { - eventImagePanel.setOpacity( 0 ); - eventImagePanel.setStyle( 'display', 'inline-block' ); + if ( eventImagePanel.getStyle('display') == 'none' ) { + eventImagePanel.setOpacity(0); + eventImagePanel.setStyle('display', 'inline-block'); new Fx.Tween( eventImagePanel, {duration: 500, transition: Fx.Transitions.Sine} ).start( 'opacity', 0, 1 ); } @@ -696,29 +717,29 @@ function loadEventImage( event, frame ) { 'height': $j('#eventThumbs').height() - $j('#eventImageBar').outerHeight(true)-10 } ); - $('eventImageNo').set( 'text', frame.FrameId ); + $('eventImageNo').set('text', frame.FrameId); $('prevImageBtn').disabled = (frame.FrameId==1); $('nextImageBtn').disabled = (frame.FrameId==event.Frames); } } function hideEventImageComplete() { - var thumbImg = $('eventThumb'+$('eventImage').getProperty( 'alt' )); + var thumbImg = $('eventThumb'+$('eventImage').getProperty('alt')); if ( thumbImg ) { thumbImg.removeClass('selected'); - thumbImg.setOpacity( 1.0 ); + thumbImg.setOpacity(1.0); } else { - console.log("Unable to find eventThumb at " + 'eventThumb'+$('eventImage').getProperty( 'alt' ) ); + console.log('Unable to find eventThumb at eventThumb'+$('eventImage').getProperty('alt')); } $('prevImageBtn').disabled = true; $('nextImageBtn').disabled = true; - $('eventImagePanel').setStyle( 'display', 'none' ); - $('eventImageStats').addClass( 'hidden' ); + $('eventImagePanel').setStyle('display', 'none'); + $('eventImageStats').addClass('hidden'); } function hideEventImage() { - if ( $('eventImagePanel').getStyle( 'display' ) != 'none' ) { - new Fx.Tween( $('eventImagePanel'), {duration: 500, transition: Fx.Transitions.Sine, onComplete: hideEventImageComplete} ).start( 'opacity', 1, 0 ); + if ( $('eventImagePanel').getStyle('display') != 'none' ) { + new Fx.Tween( $('eventImagePanel'), {duration: 500, transition: Fx.Transitions.Sine, onComplete: hideEventImageComplete} ).start('opacity', 1, 0); } } @@ -745,15 +766,15 @@ function resetEventStills() { } } -function getFrameResponse( respObj, respText ) { - if ( checkStreamForErrors( "getFrameResponse", respObj ) ) { +function getFrameResponse(respObj, respText) { + if ( checkStreamForErrors('getFrameResponse', respObj) ) { return; } var frame = respObj.frameimage; if ( !eventData ) { - console.error( "No event "+frame.EventId+" found" ); + console.error('No event '+frame.EventId+' found'); return; } @@ -763,21 +784,21 @@ function getFrameResponse( respObj, respText ) { eventData['frames'][frame.FrameId] = frame; - loadEventThumb( eventData, frame, respObj.loopback=="true" ); + loadEventThumb(eventData, frame, respObj.loopback=="true"); } var frameReq = new Request.JSON( {url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'chain', onSuccess: getFrameResponse} ); function frameQuery( eventId, frameId, loadImage ) { var parms = "view=request&request=status&entity=frameimage&id[0]="+eventId+"&id[1]="+frameId+"&loopback="+loadImage; - frameReq.send( parms ); + frameReq.send(parms); } var currFrameId = null; function checkFrames( eventId, frameId, loadImage ) { if ( !eventData ) { - console.error( "No event "+eventId+" found" ); + console.error("No event "+eventId+" found"); return; } @@ -798,29 +819,30 @@ function checkFrames( eventId, frameId, loadImage ) { for ( var fid = loFid; fid <= hiFid; fid++ ) { if ( !$('eventThumb'+fid) ) { - var img = new Element( 'img', {'id': 'eventThumb'+fid, 'src': 'graphics/transparent.png', 'alt': fid, 'class': 'placeholder'} ); - img.addEvent( 'click', function() { - eventData['frames'][fid] = null; checkFrames( eventId, fid ); - } ); - frameQuery( eventId, fid, loadImage && (fid == frameId) ); - var imgs = $('eventThumbs').getElements( 'img' ); + var img = new Element('img', {'id': 'eventThumb'+fid, 'src': 'graphics/transparent.png', 'alt': fid, 'class': 'placeholder'}); + img.addEvent('click', function() { + eventData['frames'][fid] = null; + checkFrames(eventId, fid); + }); + frameQuery(eventId, fid, loadImage && (fid == frameId)); + var imgs = $('eventThumbs').getElements('img'); var injected = false; if ( fid < imgs.length ) { - img.inject( imgs[fid-1], 'before' ); + img.inject(imgs[fid-1], 'before'); injected = true; } else { injected = imgs.some( function( thumbImg, index ) { - if ( parseInt(img.getProperty( 'alt' )) < parseInt(thumbImg.getProperty( 'alt' )) ) { - img.inject( thumbImg, 'before' ); - return ( true ); + if ( parseInt(img.getProperty('alt')) < parseInt(thumbImg.getProperty('alt')) ) { + img.inject(thumbImg, 'before'); + return true; } - return ( false ); + return false; } ); } if ( !injected ) { - img.inject( $('eventThumbs') ); + img.inject($('eventThumbs')); } var scale = parseInt(img.getStyle('height')); img.setStyles( { @@ -895,12 +917,15 @@ function getActResponse( respObj, respText ) { var actReq = new Request.JSON( {url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getActResponse} ); -function actQuery( action, parms ) { +function actQuery(action, parms) { var actParms = "view=request&request=event&id="+eventData.Id+"&action="+action; - if ( parms != null ) { - actParms += "&"+Object.toQueryString( parms ); + if ( auth_hash ) { + actParms += '&auth='+auth_hash; } - actReq.send( actParms ); + if ( parms != null ) { + actParms += "&"+Object.toQueryString(parms); + } + actReq.send(actParms); } function deleteEvent() { @@ -914,7 +939,7 @@ function deleteEvent() { getActResponse(respObj, respText); // We must wait for the deletion to happen before navigating to the next // event or this request will be cancelled. - streamNext( true ); + streamNext(true); }, }); deleteReq.send("view=request&request=event&id="+eventData.Id+"&action=delete"); @@ -922,43 +947,43 @@ function deleteEvent() { function renameEvent() { var newName = $('eventName').get('value'); - actQuery( 'rename', {eventName: newName} ); + actQuery('rename', {eventName: newName}); } function editEvent() { - createPopup( '?view=eventdetail&eid='+eventData.Id, 'zmEventDetail', 'eventdetail' ); + createPopup('?view=eventdetail&eid='+eventData.Id, 'zmEventDetail', 'eventdetail'); } function exportEvent() { - createPopup( '?view=export&eid='+eventData.Id, 'zmExport', 'export' ); + createPopup('?view=export&eid='+eventData.Id, 'zmExport', 'export'); } function archiveEvent() { - actQuery( 'archive' ); + actQuery('archive'); } function unarchiveEvent() { - actQuery( 'unarchive' ); + actQuery('unarchive'); } function showEventFrames() { - createPopup( '?view=frames&eid='+eventData.Id, 'zmFrames', 'frames', WEB_LIST_THUMB_WIDTH, WEB_LIST_THUMB_HEIGHT ); + createPopup('?view=frames&eid='+eventData.Id, 'zmFrames', 'frames', WEB_LIST_THUMB_WIDTH, WEB_LIST_THUMB_HEIGHT); } function showStream() { - $('eventStills').addClass( 'hidden' ); - $('eventVideo').removeClass( 'hidden' ); + $('eventStills').addClass('hidden'); + $('eventVideo').removeClass('hidden'); - $('stillsEvent').removeClass( 'hidden' ); - $('streamEvent').addClass( 'hidden' ); + $('stillsEvent').removeClass('hidden'); + $('streamEvent').addClass('hidden'); streamMode = 'video'; if (scale == 'auto') changeScale(); } function showStills() { - $('eventStills').removeClass( 'hidden' ); - $('eventVideo').addClass( 'hidden' ); + $('eventStills').removeClass('hidden'); + $('eventVideo').addClass('hidden'); if (vid && ( vid.paused != true ) ) { // Pause the video @@ -969,14 +994,14 @@ function showStills() { //playButton.innerHTML = "Play"; } - $('stillsEvent').addClass( 'hidden' ); - $('streamEvent').removeClass( 'hidden' ); + $('stillsEvent').addClass('hidden'); + $('streamEvent').removeClass('hidden'); streamMode = 'stills'; pauseClicked(); if ( !scroll ) { - scroll = new Fx.Scroll( 'eventThumbs', { + scroll = new Fx.Scroll('eventThumbs', { wait: false, duration: 500, offset: {'x': 0, 'y': 0}, @@ -990,17 +1015,17 @@ function showStills() { function showFrameStats() { var fid = $('eventImageNo').get('text'); - createPopup( '?view=stats&eid='+eventData.Id+'&fid='+fid, 'zmStats', 'stats', eventData.Width, eventData.Height ); + createPopup('?view=stats&eid='+eventData.Id+'&fid='+fid, 'zmStats', 'stats', eventData.Width, eventData.Height); } function videoEvent() { - createPopup( '?view=video&eid='+eventData.Id, 'zmVideo', 'video', eventData.Width, eventData.Height ); + createPopup('?view=video&eid='+eventData.Id, 'zmVideo', 'video', eventData.Width, eventData.Height); } // Called on each event load because each event can be a different width function drawProgressBar() { var barWidth = $j('#evtStream').width(); - $j('#progressBar').css( 'width', barWidth ); + $j('#progressBar').css('width', barWidth); } // Shows current stream progress. @@ -1023,8 +1048,8 @@ function progressBarNav() { function handleClick( event ) { var target = event.target; - if (vid) { - if (target.id != 'videoobj') return; //ignore clicks on control bar + if ( vid ) { + if (target.id != 'videoobj') return; // ignore clicks on control bar var x = event.offsetX; var y = event.offsetY; } else { @@ -1032,9 +1057,9 @@ function handleClick( event ) { var y = event.page.y - $(target).getTop(); } - if (event.shift || event.shiftKey) {//handle both jquery and mootools + if ( event.shift || event.shiftKey ) { // handle both jquery and mootools streamPan(x, y); - } else if (vid && event.ctrlKey) { //allow zoom out by control click. useful in fullscreen + } else if ( vid && event.ctrlKey ) { // allow zoom out by control click. useful in fullscreen vjsPanZoom('zoomOut', x, y); } else { streamZoomIn(x, y); @@ -1049,21 +1074,28 @@ function initPage() { $j('.vjs-progress-control').append('
');//add a place for videojs only on first load vid.on('ended', vjsReplay); vid.on('play', vjsPlay); - vid.on('pause', vjsPause); + vid.on('pause', pauseClicked); vid.on('click', function(event) { handleClick(event); }); + vid.on('volumechange', function() { + Cookie.write('volume', vid.volume(), {duration: 10*365}); + }); + if ( Cookie.read('volume') != null ) { + vid.volume(Cookie.read('volume')); + } vid.on('timeupdate', function() { $j('#progressValue').html(secsToTime(Math.floor(vid.currentTime()))); }); - if ( rate > 1 ) { + // rate is in % so 100 would be 1x + if ( rate > 0 ) { // rate should be 100 = 1x, etc. vid.playbackRate(rate/100); } } else { progressBarNav(); - streamCmdTimer = streamQuery.delay( 250 ); + streamCmdTimer = streamQuery.delay(250); if ( canStreamNative ) { var imageFeed = $('imageFeed'); if ( !imageFeed ) { @@ -1073,16 +1105,19 @@ function initPage() { if ( !streamImg ) { streamImg = imageFeed.getElement('object'); } - $(streamImg).addEvent( 'click', function( event ) { - handleClick( event ); - } ); + $(streamImg).addEvent('click', function(event) { + handleClick(event); + }); } } } nearEventsQuery(eventData.Id); initialAlarmCues(eventData.Id); //call ajax+renderAlarmCues - if (scale == "auto") changeScale(); + if ( scale == '0' || scale == 'auto' ) changeScale(); + document.querySelectorAll('select[name="rate"]').forEach(function(el) { + el.onchange = window['changeRate']; + }); } // Kick everything off -window.addEventListener( 'DOMContentLoaded', initPage ); +window.addEventListener('DOMContentLoaded', initPage); diff --git a/web/skins/classic/views/js/event.js.php b/web/skins/classic/views/js/event.js.php index 5df0be70d..b3f746e07 100644 --- a/web/skins/classic/views/js/event.js.php +++ b/web/skins/classic/views/js/event.js.php @@ -1,3 +1,15 @@ + // // Import constants // @@ -34,8 +46,9 @@ var eventData = { StartTime: 'StartTime() ?>', EndTime: 'EndTime() ?>', Frames: 'Frames() ?>', - MonitorName: 'Name() ?>' + MonitorName: 'Name()) ?>' }; +var monitorUrl = 'Storage()->Server()->UrlToIndex(); ?>'; var filterQuery = ''; var sortQuery = ''; @@ -45,7 +58,7 @@ var rate = ''; // really only used when setting up initial pl var scale = ""; var LabelFormat = "LabelFormat())?>"; -var canEditEvents = ; +var canEditEvents = ; var streamTimeout = ; var canStreamNative = ; @@ -54,8 +67,8 @@ var streamMode = ''; // // Strings // -var deleteString = ""; -var causeString = ""; +var deleteString = ""; +var causeString = ""; var WEB_LIST_THUMB_WIDTH = ''; var WEB_LIST_THUMB_HEIGHT = ''; var popup = ''; diff --git a/web/skins/classic/views/js/events.js b/web/skins/classic/views/js/events.js index ca7f7c7ec..9334927f0 100644 --- a/web/skins/classic/views/js/events.js +++ b/web/skins/classic/views/js/events.js @@ -1,177 +1,276 @@ -function closeWindows() { - window.close(); - // This is a hack. The only way to close an existing window is to try and open it! - var filterWindow = window.open( thisUrl+'?view=none', 'zmFilter', 'width=1,height=1' ); - filterWindow.close(); -} - -function setButtonStates( element ) { - var form = element.form; - var checked = element.checked; - form.viewBtn.disabled = !(canViewEvents && checked); - form.editBtn.disabled = !(canEditEvents && checked); - form.archiveBtn.disabled = unarchivedEvents?!checked:true; - form.unarchiveBtn.disabled = !(canEditEvents && archivedEvents && checked); - form.downloadBtn.disabled = !(canViewEvents && checked); - form.exportBtn.disabled = !(canViewEvents && checked); - form.deleteBtn.disabled = !(canEditEvents && checked); -} - -function configureButton(event) { - var element = event.target; - var form = element.form; - var checked = element.checked; - if ( !checked ) { - for (var i = 0, len=form.elements.length; i < len; i++) { - if ( form.elements[i].name.indexOf('eids') == 0) { - if ( form.elements[i].checked ) { - checked = true; - break; - } - } - } - } - if ( !element.checked ) { - form.toggleCheck.checked = false; - } - form.viewBtn.disabled = !(canViewEvents && checked); - form.editBtn.disabled = !(canEditEvents && checked); - form.archiveBtn.disabled = (!checked)||(!unarchivedEvents); - form.unarchiveBtn.disabled = !(canEditEvents && checked && archivedEvents); - form.downloadBtn.disabled = !(canViewEvents && checked); - form.exportBtn.disabled = !(canViewEvents && checked); - form.deleteBtn.disabled = !(canEditEvents && checked); -} - -function deleteEvents( element ) { - if ( ! canEditEvents ) { - alert("You do not have permission to delete events."); - return; - } - var form = element.form; - - var count = 0; - // This is slightly more efficient than a jquery selector because we stop after finding one. - for (var i = 0; i < form.elements.length; i++) { - if (form.elements[i].name.indexOf('eids') == 0) { - if ( form.elements[i].checked ) { - count++; - break; - } - } - } - if ( count > 0 ) { - if ( confirm(confirmDeleteEventsString) ) { - form.elements['action'].value = 'delete'; - form.submit(); - } - } -} - -function editEvents( element ) { - if ( ! canEditEvents ) { - alert("You do not have permission to delete events."); - return; - } - var form = element.form; - var eids = new Array(); - for (var i = 0, len=form.elements.length; i < len; i++) { - if (form.elements[i].name.indexOf('eids') == 0) { - if ( form.elements[i].checked ) { - eids[eids.length] = 'eids[]='+form.elements[i].value; - } - } - } - createPopup('?view=eventdetail&'+eids.join('&'), 'zmEventDetail', 'eventdetail'); -} - -function downloadVideo( element ) { - var form = element.form; - var eids = new Array(); - for (var i = 0, len=form.elements.length; i < len; i++) { - if (form.elements[i].name.indexOf('eids') == 0 ) { - if ( form.elements[i].checked ) { - eids[eids.length] = 'eids[]='+form.elements[i].value; - } - } - } - createPopup( '?view=download&'+eids.join('&'), 'zmDownload', 'download' ); -} - -function exportEvents( element ) { - var form = element.form; - console.log(form); - form.action = '?view=export'; - form.elements['view'].value='export'; - form.submit(); -} - -function viewEvents( element ) { - var form = element.form; - var events = new Array(); - for (var i = 0, len=form.elements.length; i < len; i++) { - if ( form.elements[i].name.indexOf('eids') == 0 ) { - if ( form.elements[i].checked ) { - events[events.length] = form.elements[i].value; - } - } - } - if ( events.length > 0 ) { - var filter = '&filter[Query][terms][0][attr]=Id&filter[Query][terms][0][op]=%3D%5B%5D&filter[Query][terms][0][val]='+events.join('%2C'); - window.location.href = thisUrl+'?view=event&eid='+events[0]+filter+sortQuery+'&page=1&play=1'; - } -} - -function archiveEvents(element) { - var form = element.form; - form.elements['action'].value = 'archive'; - form.submit(); -} - -function unarchiveEvents(element) { - if ( ! canEditEvents ) { - alert("You do not have permission to delete events."); - return; - } - var form = element.form; - form.elements['action'].value = 'unarchive'; - form.submit(); -} - -if ( openFilterWindow ) { - //opener.location.reload(true); - createPopup( '?view=filter&page='+thisPage+filterQuery, 'zmFilter', 'filter' ); - location.replace( '?view='+currentView+'&page='+thisPage+filterQuery ); -} - 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'); } -function initPage() { - if ( window.history.length == 1 ) { - $j('#controls').children().eq(0).html(''); - } +function initThumbAnimation() { $j('.colThumbnail img').each(function() { this.addEventListener('mouseover', thumbnail_onmouseover, false); this.addEventListener('mouseout', thumbnail_onmouseout, false); }); - $j('input[name=eids\\[\\]]').each(function() { - this.addEventListener('click', configureButton, false); - }); - document.getElementById("refreshLink").addEventListener("click", function onRefreshClick(evt) { - evt.preventDefault(); - window.location.reload(true); - }); - document.getElementById("backLink").addEventListener("click", function onBackClick(evt) { - evt.preventDefault(); - window.history.back(); +} + +// Returns the event id's of the selected rows +function getIdSelections() { + var table = $j('#eventTable'); + + return $j.map(table.bootstrapTable('getSelections'), function(row) { + return row.Id.replace(/(<([^>]+)>)/gi, ''); // strip the html from the element before sending }); } -$j(document).ready(initPage); +// Returns a boolen to indicate at least one selected row is archived +function getArchivedSelections() { + var table = $j('#eventTable'); + var selection = $j.map(table.bootstrapTable('getSelections'), function(row) { + return row.Archived; + }); + return selection.includes("Yes"); +} + +// Load the Delete Confirmation Modal HTML via Ajax call +function getDelConfirmModal() { + $j.getJSON(thisUrl + '?request=modal&modal=delconfirm') + .done(function(data) { + if ( $j('#deleteConfirm').length ) { + $j('#deleteConfirm').replaceWith(data.html); + } else { + $j("body").append(data.html); + } + manageDelConfirmModalBtns(); + }) + .fail(logAjaxFail); +} + +// Manage the DELETE CONFIRMATION modal button +function manageDelConfirmModalBtns() { + document.getElementById("delConfirmBtn").addEventListener("click", function onDelConfirmClick(evt) { + if ( ! canEditEvents ) { + enoperm(); + return; + } + + var selections = getIdSelections(); + + evt.preventDefault(); + $j.getJSON(thisUrl + '?request=events&action=delete&eids[]='+selections.join('&eids[]=')) + .done( function(data) { + $j('#eventTable').bootstrapTable('refresh'); + window.location.reload(true); + }) + .fail(logAjaxFail); + }); + + // Manage the CANCEL modal button + document.getElementById("delCancelBtn").addEventListener("click", function onDelCancelClick(evt) { + $j('#deleteConfirm').modal('hide'); + }); +} + +function getEventDetailModal(eid) { + $j.getJSON(thisUrl + '?request=modal&modal=eventdetail&eids[]=' + eid) + .done(function(data) { + if ( $j('#eventDetailModal').length ) { + $j('#eventDetailModal').replaceWith(data.html); + } else { + $j("body").append(data.html); + } + $j('#eventDetailModal').modal('show'); + // Manage the Save button + $j('#eventDetailSaveBtn').click(function(evt) { + evt.preventDefault(); + $j('#eventDetailForm').submit(); + }); + }) + .fail(logAjaxFail); +} + +function initPage() { + var backBtn = $j('#backBtn'); + var viewBtn = $j('#viewBtn'); + var archiveBtn = $j('#archiveBtn'); + var unarchiveBtn = $j('#unarchiveBtn'); + var editBtn = $j('#editBtn'); + var exportBtn = $j('#exportBtn'); + var downloadBtn = $j('#downloadBtn'); + var deleteBtn = $j('#deleteBtn'); + var table = $j('#eventTable'); + + // Load the delete confirmation modal into the DOM + getDelConfirmModal(); + + // Init the bootstrap-table + table.bootstrapTable({icons: icons}); + + // Hide these columns on first run when no cookie is saved + if ( !getCookie("zmEventsTable.bs.table.columns") ) { + table.bootstrapTable('hideColumn', 'Archived'); + table.bootstrapTable('hideColumn', 'Emailed'); + } + + // enable or disable buttons based on current selection and user rights + table.on('check.bs.table uncheck.bs.table ' + + 'check-all.bs.table uncheck-all.bs.table', + function() { + selections = table.bootstrapTable('getSelections'); + + viewBtn.prop('disabled', !(selections.length && canViewEvents)); + archiveBtn.prop('disabled', !(selections.length && canEditEvents)); + unarchiveBtn.prop('disabled', !(getArchivedSelections()) && canEditEvents); + editBtn.prop('disabled', !(selections.length && canEditEvents)); + exportBtn.prop('disabled', !(selections.length && canViewEvents)); + downloadBtn.prop('disabled', !(selections.length && canViewEvents)); + deleteBtn.prop('disabled', !(selections.length && canEditEvents)); + }); + + // Don't enable the back button if there is no previous zm page to go back to + backBtn.prop('disabled', !document.referrer.length); + + // Setup the thumbnail video animation + initThumbAnimation(); + + // Some toolbar events break the thumbnail animation, so re-init eventlistener + table.on('all.bs.table', initThumbAnimation); + + // Manage the BACK button + document.getElementById("backBtn").addEventListener("click", function onBackClick(evt) { + evt.preventDefault(); + window.history.back(); + }); + + // Manage the REFRESH Button + document.getElementById("refreshBtn").addEventListener("click", function onRefreshClick(evt) { + evt.preventDefault(); + window.location.reload(true); + }); + + // Manage the TIMELINE Button + document.getElementById("tlineBtn").addEventListener("click", function onTlineClick(evt) { + evt.preventDefault(); + window.location.assign('?view=timeline'+filterQuery); + }); + + // Manage the VIEW button + document.getElementById("viewBtn").addEventListener("click", function onViewClick(evt) { + var selections = getIdSelections(); + + evt.preventDefault(); + var filter = '&filter[Query][terms][0][attr]=Id&filter[Query][terms][0][op]=%3D%5B%5D&filter[Query][terms][0][val]='+selections.join('%2C'); + window.location.href = thisUrl+'?view=event&eid='+selections[0]+filter+sortQuery+'&page=1&play=1'; + }); + + // Manage the ARCHIVE button + document.getElementById("archiveBtn").addEventListener("click", function onArchiveClick(evt) { + var selections = getIdSelections(); + + evt.preventDefault(); + $j.getJSON(thisUrl + '?request=events&action=archive&eids[]='+selections.join('&eids[]=')) + .done( function(data) { + $j('#eventTable').bootstrapTable('refresh'); + window.location.reload(true); + }) + .fail(logAjaxFail); + }); + + // Manage the UNARCHIVE button + document.getElementById("unarchiveBtn").addEventListener("click", function onUnarchiveClick(evt) { + if ( ! canEditEvents ) { + enoperm(); + return; + } + + var selections = getIdSelections(); + console.log(selections); + + evt.preventDefault(); + $j.getJSON(thisUrl + '?request=events&action=unarchive&eids[]='+selections.join('&eids[]=')) + .done( function(data) { + $j('#eventTable').bootstrapTable('refresh'); + window.location.reload(true); + }) + .fail(logAjaxFail); + + if ( openFilterWindow ) { + //opener.location.reload(true); + createPopup( '?view=filter&page='+thisPage+filterQuery, 'zmFilter', 'filter' ); + location.replace( '?view='+currentView+'&page='+thisPage+filterQuery ); + } else { + window.location.reload(true); + } + }); + + // Manage the EDIT button + document.getElementById("editBtn").addEventListener("click", function onEditClick(evt) { + if ( ! canEditEvents ) { + enoperm(); + return; + } + + var selections = getIdSelections(); + + evt.preventDefault(); + $j.getJSON(thisUrl + '?request=modal&modal=eventdetail&eids[]='+selections.join('&eids[]=')) + .done(function(data) { + if ( $j('#eventDetailModal').length ) { + $j('#eventDetailModal').replaceWith(data.html); + } else { + $j("body").append(data.html); + } + $j('#eventDetailModal').modal('show'); + // Manage the Save button + $j('#eventDetailSaveBtn').click(function(evt) { + evt.preventDefault(); + $j('#eventDetailForm').submit(); + }); + }) + .fail(logAjaxFail); + }); + + // Manage the EXPORT button + document.getElementById("exportBtn").addEventListener("click", function onExportClick(evt) { + var selections = getIdSelections(); + + evt.preventDefault(); + window.location.assign('?view=export&eids[]='+selections.join('&eids[]=')); + }); + + // Manage the DOWNLOAD VIDEO button + document.getElementById("downloadBtn").addEventListener("click", function onDownloadClick(evt) { + var selections = getIdSelections(); + + evt.preventDefault(); + createPopup('?view=download&eids[]='+selections.join('&eids[]='), 'zmDownload', 'download'); + }); + + // Manage the DELETE button + document.getElementById("deleteBtn").addEventListener("click", function onDeleteClick(evt) { + if ( ! canEditEvents ) { + enoperm(); + return; + } + + evt.preventDefault(); + $j('#deleteConfirm').modal('show'); + }); + + // Manage the eventdetail links in the events list + $j(".eDetailLink").click(function(evt) { + evt.preventDefault(); + var eid = $j(this).data('eid'); + getEventDetailModal(eid); + }); + + // The table is initially given a hidden style, so now that we are done rendering, show it + table.show(); +} + +$j(document).ready(function() { + initPage(); +}); diff --git a/web/skins/classic/views/js/events.js.php b/web/skins/classic/views/js/events.js.php index 8a49c00c3..0b96f12c5 100644 --- a/web/skins/classic/views/js/events.js.php +++ b/web/skins/classic/views/js/events.js.php @@ -1,9 +1,10 @@ + //var openFilterWindow = ; var openFilterWindow = false; -var archivedEvents = ; -var unarchivedEvents = ; - var filterQuery = ''; var sortQuery = ''; diff --git a/web/skins/classic/views/js/export.js b/web/skins/classic/views/js/export.js index 74e7d029b..dfe74ebe3 100644 --- a/web/skins/classic/views/js/export.js +++ b/web/skins/classic/views/js/export.js @@ -1,22 +1,31 @@ var exportTimer = null; -function configureExportButton( element ) { +function configureExportButton(element) { var form = element.form; - var checkCount = 0; - var radioCount = 0; - for ( var i = 0; i < form.elements.length; i++ ) { - if ( form.elements[i].type == "checkbox" && form.elements[i].checked ) { - checkCount++; - } else if ( form.elements[i].type == "radio" && form.elements[i].checked ) { - radioCount++; + var eventCount = 0; + document.querySelectorAll('input[name="eids[]"]').forEach(function(el) { + if ( el.checked ) { + eventCount ++; } - } - form.elements['exportButton'].disabled = (checkCount == 0 || radioCount == 0); + }); + + form.elements['exportButton'].disabled = ( + eventCount && + ( + form.elements['exportDetail'].checked || + form.elements['exportFrames'].checked || + form.elements['exportImages'].checked || + form.elements['exportVideo'].checked || + form.elements['exportMisc'].checked + ) && + ( form.elements['exportFormat'][0].checked || form.elements['exportFormat'][1].checked ) && + ( form.elements['exportCompress'][0].checked || form.elements['exportCompress'][1].checked ) + ); } function startDownload(file) { - console.log("Starting download of " + file); + console.log('Starting download of ' + file); window.location.replace(file); } @@ -32,17 +41,12 @@ function exportProgress() { } function exportResponse(respObj, respText) { - //console.log(respText); - - $clear(exportTimer); + clearInterval(exportTimer); if ( respObj.result != 'Ok' ) { $('exportProgressTicker').set('text', respObj.message); } else { $('exportProgressTicker').set('text', exportSucceededString); - console.log(respObj.exportFile); - console.log(encodeURIComponent(respObj.exportFile)); - - startDownload.pass(decodeURIComponent(respObj.exportFile)).delay( 1500 ); + startDownload.pass(decodeURIComponent(respObj.exportFile)).delay(1500); } return; @@ -59,7 +63,7 @@ function exportResponse(respObj, respText) { //window.location.replace( thisUrl+'?view='+currentView+'&'+eids.join('&')+'&exportFile='+respObj.exportFile+'&generated='+((respObj.result=='Ok')?1:0) ); } -function exportEvent( ) { +function exportEvents( ) { var parms = 'view=event&request=event&action=export'; parms += '&'+$('contentForm').toQueryString(); var query = new Request.JSON( { @@ -69,19 +73,43 @@ function exportEvent( ) { onSuccess: exportResponse } ); query.send(); - $('exportProgress').removeClass( 'hidden' ); - $('exportProgress').setProperty( 'class', 'warnText' ); - $('exportProgressText').set( 'text', exportProgressString ); - exportProgress(); + $('exportProgress').removeClass('hidden'); + $('exportProgress').setProperty('class', 'warnText'); + $('exportProgressText').set('text', exportProgressString); + //exportProgress(); exportTimer = exportProgress.periodical( 500 ); } +function getEventDetailModal(eid) { + $j.getJSON(thisUrl + '?request=modal&modal=eventdetail&eids[]=' + eid) + .done(function(data) { + if ( $j('#eventDetailModal').length ) { + $j('#eventDetailModal').replaceWith(data.html); + } else { + $j("body").append(data.html); + } + $j('#eventDetailModal').modal('show'); + // Manage the Save button + $j('#eventDetailSaveBtn').click(function(evt) { + evt.preventDefault(); + $j('#eventDetailForm').submit(); + }); + }) + .fail(logAjaxFail); +} + function initPage() { configureExportButton( $('exportButton') ); if ( exportReady ) { - startDownload.pass(exportFile).delay( 1500 ); + startDownload.pass(exportFile).delay(1500); } - document.getElementById('exportButton').addEventListener('click', exportEvent); + document.getElementById('exportButton').addEventListener('click', exportEvents); + // Manage the eventdetail link in the export list + $j(".eDetailLink").click(function(evt) { + evt.preventDefault(); + var eid = $j(this).data('eid'); + getEventDetailModal(eid); + }); } window.addEventListener('DOMContentLoaded', initPage); diff --git a/web/skins/classic/views/js/filter.js b/web/skins/classic/views/js/filter.js index 99e8a9c03..163a7fdde 100644 --- a/web/skins/classic/views/js/filter.js +++ b/web/skins/classic/views/js/filter.js @@ -2,12 +2,12 @@ function selectFilter(element) { element.form.submit(); } -function validateForm( form ) { +function validateForm(form) { var rows = $j(form).find('tbody').eq(0).find('tr'); var obrCount = 0; var cbrCount = 0; for ( var i = 0; i < rows.length; i++ ) { - if (rows.length > 2) { + if ( rows.length > 2 ) { obrCount += parseInt(form.elements['filter[Query][terms][' + i + '][obr]'].value); cbrCount += parseInt(form.elements['filter[Query][terms][' + i + '][cbr]'].value); } @@ -22,21 +22,67 @@ function validateForm( form ) { } var numbers_reg = /\D/; if ( numbers_reg.test(form.elements['filter[Query][limit]'].value) ) { - alert("There appear to be non-numeric characters in your limit. Limit must be a positive integer value or empty."); + alert('There appear to be non-numeric characters in your limit. Limit must be a positive integer value or empty.'); return false; } + if ( form.elements['filter[AutoDelete]'].checked ) { + // if Delete action is Enabled should also have an unarchived term + var have_archivestatus_term = false; + for ( var i = 0; i < rows.length; i++ ) { + if ( form.elements['filter[Query][terms][' + i + '][attr]'].value == 'Archived' ) { + have_archivestatus_term = true; + } + } + if ( ! have_archivestatus_term ) { + return confirm('You have enabled deleting events but do not have a term referencing the archived status of the event. This filter may delete events that you want to save! Are you sure?'); + } + } else if ( form.elements['filter[UpdateDiskSpace]'].checked ) { + var have_endtime_term = false; + for ( var i = 0; i < rows.length; i++ ) { + if ( + ( form.elements['filter[Query][terms][' + i + '][attr]'].value == 'EndDateTime' ) + || + ( form.elements['filter[Query][terms][' + i + '][attr]'].value == 'EndTime' ) + || + ( form.elements['filter[Query][terms][' + i + '][attr]'].value == 'EndDate' ) + ) { + have_endtime_term = true; + break; + } + } + if ( ! have_endtime_term ) { + return confirm('You don\'t have an End Date/Time term in your filter. This might match recordings that are still in progress and so the UpdateDiskSpace action will be a waste of time and resources. Ideally you should have an End Date/Time IS NOT NULL term. Do you want to continue?'); + } + } else if ( form.elements['filter[Background]'].checked ) { + if ( ! ( + form.elements['filter[AutoArchive]'].checked || + form.elements['filter[UpdateDiskSpace]'].checked || + form.elements['filter[AutoVideo]'].checked || + form.elements['filter[AutoEmail]'].checked || + form.elements['filter[AutoMessage]'].checked || + form.elements['filter[AutoExecute]'].checked || + form.elements['filter[AutoDelete]'].checked || + form.elements['filter[AutoCopy]'].checked || + form.elements['filter[AutoMove]'].checked + ) ) { + alert('You have chosen to run this filter in the background but not selected any actions.'); + } + } return true; } function updateButtons(element) { var form = element.form; - if ( element.type == 'checkbox' && element.checked ) { form.elements['executeButton'].disabled = false; } else { var canExecute = false; if ( form.elements['filter[AutoArchive]'] && form.elements['filter[AutoArchive]'].checked ) { canExecute = true; + } else if ( form.elements['filter[AutoCopy]'] && form.elements['filter[AutoCopy]'].checked ) { + canExecute = true; + } else if ( form.elements['filter[AutoMove]'] && form.elements['filter[AutoMove]'].checked ) { + canExecute = true; } else if ( form.elements['filter[AutoVideo]'] && form.elements['filter[AutoVideo]'].checked ) { canExecute = true; } else if ( form.elements['filter[AutoUpload]'] && form.elements['filter[AutoUpload]'].checked ) { @@ -63,6 +109,15 @@ function updateButtons(element) { } } +function click_AutoEmail(element) { + updateButtons(this); + if ( this.checked ) { + $j('#EmailOptions').show(); + } else { + $j('#EmailOptions').hide(); + } +} + function click_automove(element) { updateButtons(this); if ( this.checked ) { @@ -96,15 +151,21 @@ function resetFilter( element ) { $j('#contentForm')[0].reset(); } -function submitToEvents( element ) { +function submitToEvents(element) { var form = element.form; - form.action = thisUrl + '?view=events'; - history.replaceState(null, null, '?view=filter&' + $j(form).serialize()); + //form.action = '?view=events'; + //form.submit(); + //console.log(form); + //console.log($j(form).serialize()); + //history.replaceState(null, null, '?view=filter&' + $j(form).serialize()); + window.location.assign('?view=events&'+$j(form).serialize()); } -function submitToMontageReview( element ) { + +function submitToMontageReview(element) { var form = element.form; form.action = thisUrl + '?view=montagereview'; - history.replaceState(null, null, '?view=filter&' + $j(form).serialize()); + window.location.assign('?view=montagereview&'+$j(form).serialize()); + history.replaceState(null, null, '?view=montagereview&live=0&' + $j(form).serialize()); } function submitToExport(element) { @@ -137,14 +198,19 @@ function deleteFilter( element ) { form.submit(); } } +var escape = document.createElement('textarea'); +function escapeHTML(html) { + escape.textContent = html; + return escape.innerHTML; +} function parseRows(rows) { - for (var rowNum = 0; rowNum < rows.length; rowNum++) { //Each row is a term + for ( var rowNum = 0; rowNum < rows.length; rowNum++ ) { //Each row is a term var queryPrefix = 'filter[Query][terms]['; var inputTds = rows.eq(rowNum).children(); - if (rowNum == 0) inputTds.eq(0).html(' '); //Remove and from first term - if (rowNum > 0) { //add and/or to 1+ + if ( rowNum == 0 ) inputTds.eq(0).html(' '); //Remove and from first term + if ( rowNum > 0 ) { //add and/or to 1+ var cnjVal = inputTds.eq(0).children().val(); var conjSelect = $j('').attr('name', queryPrefix + rowNum + '][cnj]').attr('id', queryPrefix + rowNum + '][cnj]'); $j.each(conjTypes, function(i) { @@ -154,33 +220,33 @@ function parseRows(rows) { } var brackets = rows.length - 2; - if (brackets > 0) { //add bracket select to all rows + if ( brackets > 0 ) { // add bracket select to all rows var obrSelect = $j('').attr('name', queryPrefix + rowNum + '][obr]').attr('id', queryPrefix + rowNum + '][obr]'); var cbrSelect = $j('').attr('name', queryPrefix + rowNum + '][cbr]').attr('id', queryPrefix + rowNum + '][cbr]'); obrSelect.append(''); cbrSelect.append(''); } - var obrVal = inputTds.eq(1).children().val() != undefined ? inputTds.eq(1).children().val() : 0; //Save currently selected bracket option + var obrVal = inputTds.eq(1).children().val() != undefined ? inputTds.eq(1).children().val() : 0; // Save currently selected bracket option var cbrVal = inputTds.eq(5).children().val() != undefined ? inputTds.eq(5).children().val() : 0; - inputTds.eq(1).html(obrSelect).children().val(obrVal); //Set bracket contents and assign saved value + inputTds.eq(1).html(obrSelect).children().val(obrVal); // Set bracket contents and assign saved value inputTds.eq(5).html(cbrSelect).children().val(cbrVal); } else { - inputTds.eq(1).html(' '); //Blank if there aren't enough terms for brackets + inputTds.eq(1).html(' '); // Blank if there aren't enough terms for brackets inputTds.eq(5).html(' '); } - if (rows.length == 1) { - inputTds.eq(6).find(':input[value="-"]').prop('disabled', true); //enable/disable remove row button + if ( rows.length == 1 ) { + inputTds.eq(6).find('button[data-on-click-this="delTerm"]').prop('disabled', true); // enable/disable remove row button } else { - inputTds.eq(6).find(':input[value="-"]').prop('disabled', false); + inputTds.eq(6).find('button[data-on-click-this="delTerm"]').prop('disabled', false); } var attr = inputTds.eq(2).children().val(); - if ( attr == "Archived") { //Archived types + if ( attr == 'Archived' ) { //Archived types inputTds.eq(3).html('equal to'); var archiveSelect = $j('').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]'); for (var i = 0; i < archiveTypes.length; i++) { @@ -188,6 +254,18 @@ function parseRows(rows) { } var archiveVal = inputTds.eq(4).children().val(); inputTds.eq(4).html(archiveSelect).children().val(archiveVal).chosen({width: "101%"}); + } else if ( attr == 'AlarmedZoneId' ) { + var zoneSelect = $j('').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]'); + for ( monitor_id in monitors ) { + for ( zone_id in zones ) { + var zone = zones[zone_id]; + if ( monitor_id == zone.MonitorId ) { + zoneSelect.append(''); + } + } // end foreach zone + } // end foreach monitor + var zoneVal = inputTds.eq(4).children().val(); + inputTds.eq(4).html(zoneSelect).children().val(zoneVal).chosen({width: "101%"}); } else if ( attr.indexOf('Weekday') >= 0 ) { //Weekday selection var weekdaySelect = $j('').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]'); for (var i = 0; i < weekdays.length; i++) { @@ -218,22 +296,47 @@ function parseRows(rows) { inputTds.eq(4).html(storageSelect).children().val(storageVal).chosen({width: "101%"}); } else if ( attr == 'MonitorName' ) { //Monitor names var monitorSelect = $j('').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]'); - for (var key in monitors) { - monitorSelect.append(''); + for ( var monitor_id in monitors ) { + monitorSelect.append(''); } var monitorVal = inputTds.eq(4).children().val(); inputTds.eq(4).html(monitorSelect).children().val(monitorVal); - } else { //Reset to regular text field and operator for everything that isn't special - var opSelect = $j('').attr('name', queryPrefix + rowNum + '][op]').attr('id', queryPrefix + rowNum + '][op]'); - for (var key in opTypes) { - opSelect.append(''); + } else if ( attr == 'ExistsInFileSystem' ) { + var select = $j('').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]'); + for ( var booleanVal in booleanValues ) { + select.append(''); } - var opVal = inputTds.eq(3).children().val(); - inputTds.eq(3).html(opSelect).children().val(opVal).chosen({width: "101%"}); + var val = inputTds.eq(4).children().val(); + if ( ! val ) val = 'false'; // default to the first option false + inputTds.eq(4).html(select).children().val(val); + } else { // Reset to regular text field and operator for everything that isn't special var textInput = $j('').attr('type', 'text').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]'); var textVal = inputTds.eq(4).children().val(); inputTds.eq(4).html(textInput).children().val(textVal); } + + // Validate the operator + var opSelect = $j('').attr('name', queryPrefix + rowNum + '][op]').attr('id', queryPrefix + rowNum + '][op]'); + var opVal = inputTds.eq(3).children().val(); + if ( attr == 'ExistsInFileSystem' ) { + if ( ! opVal ) { + // Default to equals so that something gets selected + opVal = 'IS'; + } + for ( var key of ['IS', 'IS NOT'] ) { + opSelect.append(''); + } + } else { + if ( ! opVal ) { + // Default to equals so that something gets selected + console.log("No value for operator. Defaulting to ="); + opVal = '='; + } + for ( var key in opTypes ) { + opSelect.append(''); + } + } + inputTds.eq(3).html(opSelect).children().val(opVal).chosen({width: "101%"}); if ( attr.endsWith('DateTime') ) { //Start/End DateTime inputTds.eq(4).children().datetimepicker({timeFormat: "HH:mm:ss", dateFormat: "yy-mm-dd", maxDate: 0, constrainInput: false}); } else if ( attr.endsWith('Date') ) { //Start/End Date @@ -249,9 +352,9 @@ function parseRows(rows) { term[2] = rowNum; inputTds.eq(2).children().eq(0).attr('name', 'filter'+stringFilter(term)); inputTds.eq(2).children().eq(0).attr('id', 'filter'+stringFilter(term)); - }//End for each term/row + } //End for each term/row history.replaceState(null, null, '?view=filter&' + $j('#contentForm').serialize()); -} +} // parseRows function stringFilter(term) { var termString = ''; @@ -270,11 +373,16 @@ function addTerm( element ) { this[0].selected = 'selected'; }).chosen({width: '101%'}); newRow.find('input[type="text"]').val(''); - newRow[0].querySelectorAll("button[data-on-click-this]").forEach(function attachOnClick(el) { + newRow[0].querySelectorAll("button[data-on-click-this]").forEach(function(el) { var fnName = el.getAttribute("data-on-click-this"); el.onclick = window[fnName].bind(el, el); }); + newRow[0].querySelectorAll('select[data-on-change-this]').forEach(function(el) { + var fnName = el.getAttribute('data-on-change-this'); + el.onchange = window[fnName].bind(el, el); + }); + var rows = $j(row).parent().children(); parseRows(rows); } @@ -287,11 +395,53 @@ function delTerm( element ) { parseRows(rows); } +function debugFilter() { + getModal('filterdebug'); +} + +// Load the Delete Confirmation Modal HTML via Ajax call +function getModal(id) { + $j.getJSON(thisUrl + '?request=modal&modal='+id+'&fid='+filterid) + .done(function(data) { + if ( !data ) { + console.error("Get modal returned no data"); + return; + } + + if ( $j('#'+id).length ) { + console.log("replacing"); + $j('#'+id).replaceWith(data.html); + } else { + console.log("Adding to body"+data.html); + $j('body').append(data.html); + } + manageModalBtns(id); + modal = $j('#'+id+'Modal'); + if ( ! modal.length ) { + console.log("No modal found"); + } + $j('#'+id+'Modal').modal('show'); + }) + .fail(logAjaxFail); +} + +function manageModalBtns(id) { + console.log(id); + // Manage the CANCEL modal button + var cancelBtn = document.getElementById(id+"CancelBtn"); + if ( cancelBtn ) { + document.getElementById(id+"CancelBtn").addEventListener("click", function onCancelClick(evt) { + $j('#'+id).modal('hide'); + }); + } +} + function init() { updateButtons( $('executeButton') ); $j('#Id').chosen(); $j('#fieldsTable select').not("[name$='br\\]'], [name$='cnj\\]']").chosen({width: '101%'}); //Every select except brackets/and $j("#sortTable [name$='sort_field\\]']").chosen(); + parseRows($j('#fieldsTable tbody').children()); } window.addEventListener( 'DOMContentLoaded', init ); diff --git a/web/skins/classic/views/js/filter.js.php b/web/skins/classic/views/js/filter.js.php index 15db50a32..f7f7547f2 100644 --- a/web/skins/classic/views/js/filter.js.php +++ b/web/skins/classic/views/js/filter.js.php @@ -1,3 +1,19 @@ + + var filterid = 'Id() ?>'; var filterQuery = ''; var sortQuery = ''; @@ -10,6 +26,8 @@ var states = ; var servers = ; var storageareas = ; var monitors = ; +var zones = ; +var booleanValues = ; var errorBrackets = ''; var errorValue = ''; diff --git a/web/skins/classic/views/js/frame.js b/web/skins/classic/views/js/frame.js index 03058ff8a..5375bb098 100644 --- a/web/skins/classic/views/js/frame.js +++ b/web/skins/classic/views/js/frame.js @@ -11,7 +11,7 @@ function changeScale() { if ( img ) { var baseWidth = $j('#base_width').val(); var baseHeight = $j('#base_height').val(); - if ( scale == 'auto' ) { + if ( ! parseInt(scale) ) { var newSize = scaleToFit(baseWidth, baseHeight, img, $j('#controls')); newWidth = newSize.width; newHeight = newSize.height; @@ -24,16 +24,49 @@ function changeScale() { img.css('width', newWidth + 'px'); img.css('height', newHeight + 'px'); } - Cookie.write( 'zmWatchScale', scale, {duration: 10*365} ); + Cookie.write('zmWatchScale', scale, {duration: 10*365}); $j.each(controlsLinks, function(k, anchor) { //Make frames respect scale choices - anchor.prop('href', anchor.prop('href').replace(/scale=.*&/, 'scale=' + scale + '&')); + if ( anchor ) { + anchor.prop('href', anchor.prop('href').replace(/scale=.*&/, 'scale=' + scale + '&')); + } }); } -if (scale == 'auto') { +if ( !scale ) { $j(document).ready(changeScale); } document.addEventListener('DOMContentLoaded', function onDCL() { - document.getElementById('scale').addEventListener('change', changeScale); + document.getElementById('scaleControl').addEventListener('change', changeScale); +}); + +function initPage() { + var backBtn = $j('#backBtn'); + + if ( scale == '0' || scale == 'auto' ) changeScale(); + + // Don't enable the back button if there is no previous zm page to go back to + backBtn.prop('disabled', !document.referrer.length); + + // Manage the BACK button + document.getElementById("backBtn").addEventListener("click", function onBackClick(evt) { + evt.preventDefault(); + window.history.back(); + }); + + // Manage the REFRESH Button + document.getElementById("refreshBtn").addEventListener("click", function onRefreshClick(evt) { + evt.preventDefault(); + window.location.reload(true); + }); + + // Manage the STATS button + document.getElementById("statsBtn").addEventListener("click", function onViewClick(evt) { + evt.preventDefault(); + window.location.href = thisUrl+'?view=stats&eid='+eid+'&fid='+fid; + }); +} + +$j(document).ready(function() { + initPage(); }); diff --git a/web/skins/classic/views/js/frame.js.php b/web/skins/classic/views/js/frame.js.php index 572587f82..9bd2f4236 100644 --- a/web/skins/classic/views/js/frame.js.php +++ b/web/skins/classic/views/js/frame.js.php @@ -1,3 +1,15 @@ + + var scale = ''; var SCALE_BASE = ; + +var eid = ; +var fid = ; +var record_event_stats = ; +var alarmFrame = ; diff --git a/web/skins/classic/views/js/frames.js b/web/skins/classic/views/js/frames.js new file mode 100644 index 000000000..57bf8e4b0 --- /dev/null +++ b/web/skins/classic/views/js/frames.js @@ -0,0 +1,78 @@ +function thumbnail_onmouseover(event) { + var img = event.target; + img.src = ''; + img.src = img.getAttribute('full_img_src'); +} + +function thumbnail_onmouseout(event) { + var img = event.target; + img.src = ''; + img.src = img.getAttribute('img_src'); +} + +function initThumbAnimation() { + $j('.colThumbnail img').each(function() { + this.addEventListener('mouseover', thumbnail_onmouseover, false); + this.addEventListener('mouseout', thumbnail_onmouseout, false); + }); +} + +function processClicks(event, field, value, row, $element) { + if ( field == 'FrameScore' ) { + window.location.assign('?view=stats&eid='+row.EventId+'&fid='+row.FrameId); + } else { + window.location.assign('?view=frame&eid='+row.EventId+'&fid='+row.FrameId); + } +} + +// This function handles when the user clicks a "+" link to retrieve stats for a frame +function detailFormatter(index, row, $detail) { + $detail.html('Please wait. Loading from ajax request...'); + $j.get(thisUrl + '?request=stats&eid=' + row.EventId + '&fid=' + row.FrameId + '&row=' + index) + .done(function(data) { + $detail.html(data.html); + }) + .fail(logAjaxFail); +} +function initPage() { + var backBtn = $j('#backBtn'); + var table = $j('#framesTable'); + + // Init the bootstrap-table + table.bootstrapTable({icons: icons}); + + // Hide these columns on first run when no cookie is saved + if ( !getCookie("zmFramesTable.bs.table.columns") ) { + table.bootstrapTable('hideColumn', 'EventId'); + } + + // Hide the stats tables on init + $j(".contentStatsTable").hide(); + + // Disable the back button if there is nothing to go back to + backBtn.prop('disabled', !document.referrer.length); + + // Setup the thumbnail animation + initThumbAnimation(); + + // Some toolbar events break the thumbnail animation, so re-init eventlistener + table.on('all.bs.table', initThumbAnimation); + + // Load the associated frame image when the user clicks on a row + table.on('click-cell.bs.table', processClicks); + + // Manage the BACK button + document.getElementById("backBtn").addEventListener("click", function onBackClick(evt) { + evt.preventDefault(); + window.history.back(); + }); + // Manage the REFRESH Button + document.getElementById("refreshBtn").addEventListener("click", function onRefreshClick(evt) { + evt.preventDefault(); + window.location.reload(true); + }); +} + +$j(document).ready(function() { + initPage(); +}); diff --git a/web/skins/classic/views/js/group.js b/web/skins/classic/views/js/group.js index 0f0bd064b..26aa9cae6 100644 --- a/web/skins/classic/views/js/group.js +++ b/web/skins/classic/views/js/group.js @@ -1,21 +1,20 @@ -function selectMonitors() { - createPopup( '?view=monitorselect&callForm=groupForm&callField=newGroup[MonitorIds]', 'zmMonitors', 'monitorselect' ); -} - -if ( refreshParent ) { - opener.location.reload(true); -} - -function configureButtons( element ) { - if ( canEditGroups ) { - var form = element.form; - var disabled = false; - - if ( form.elements['newGroup[Name]'].value == '' ) { - disabled = true; - } - form.saveBtn.disabled = disabled; +function configureButtons() { + var form = $j('#groupForm')[0]; + if ( !form ) { + console.log("No groupForm found"); + return; } + if ( !canEditGroups ) { + console.log("Cannot edit groups"); + form.elements['action'].disabled = disabled; + return; + } + var disabled = false; + + if ( form.elements['newGroup[Name]'].value == '' ) { + disabled = true; + } + form.elements['action'].disabled = disabled; } window.focus(); diff --git a/web/skins/classic/views/js/groups.js b/web/skins/classic/views/js/groups.js index 31c9402a2..8ff1b61ff 100644 --- a/web/skins/classic/views/js/groups.js +++ b/web/skins/classic/views/js/groups.js @@ -8,8 +8,13 @@ function setGroup( element ) { form.submit(); } -function editGroup( gid ) { - createPopup( '?view=group&gid='+gid, 'zmGroup', 'group' ); +function editGroup( element ) { + var gid = element.getAttribute('data-group-id'); + if ( !gid ) { + console.log('No group id found in editGroup'); + } else { + createPopup('?view=group&gid='+gid, 'zmGroup'+gid, 'group'); + } } function deleteGroup( element ) { diff --git a/web/skins/classic/views/js/log.js b/web/skins/classic/views/js/log.js index 369462a9d..53f197eab 100644 --- a/web/skins/classic/views/js/log.js +++ b/web/skins/classic/views/js/log.js @@ -1,4 +1,4 @@ -var logParms = "view=request&request=log&task=query"; +var logParms = 'view=request&request=log&task=query'; var logReq = new Request.JSON( {url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: logResponse} ); var logTimer = undefined; var logTable = undefined; @@ -148,7 +148,7 @@ function logResponse( respObj ) { function refreshLog() { options = {}; - logTable.empty(); + $j('#logTable tbody').empty(); firstLoad = true; maxLogTime = 0; minLogTime = 0; @@ -163,15 +163,26 @@ function expandLog() { fetchPrevLogs(); } +function clearResponse() { + refreshLog(); +} +function clearError() { +} function clearLog() { logReq.cancel(); - minLogTime = 0; - logCount = 0; - logTimeout = maxSampleTime; - displayLimit = initialDisplayLimit; - $('displayLogs').set('text', logCount); - options = {}; - logTable.empty(); + + 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'); + if ( rows && rows.length ) { + var minTime = rows[0].getElement('td').get('text'); + clearParms += "&minTime="+encodeURIComponent(minTime); + var maxTime = rows[rows.length-1].getElement('td').get('text'); + clearParms += "&maxTime="+encodeURIComponent(maxTime); + } + var form = $('logForm'); + clearReq.send(clearParms+"&"+form.toQueryString()); } function filterLog() { @@ -179,9 +190,9 @@ 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 ); + window.console.log('No selector found for ' + field); } return; } @@ -199,35 +210,50 @@ function resetLog() { refreshLog(); } -var exportFormValidator; +var exportFormValidator = null; function exportLog() { - exportFormValidator.reset(); - $('exportLog').overlayShow(); + getModal('log_export'); + + if ( !exportFormValidator ) { + exportFormValidator = new Form.Validator.Inline($('exportForm'), { + useTitles: true, + warningPrefix: '', + errorPrefix: '' + }); + } else { + exportFormValidator.reset(); + } } function exportResponse( response ) { - $('exportLog').unspin(); + $('log_exportModal').unspin(); if ( response.result == 'Ok' ) { window.location.replace( thisUrl+'?view=request&request=log&task=download&key='+response.key+'&format='+response.format ); } } function exportFail( request ) { - $('exportLog').unspin(); - $('exportErrorText').set('text', request.status+" / "+request.statusText ); + $('log_exportModal').unspin(); + $('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() { var form = $('exportForm'); - $('exportErrorText').set('text', "" ); + console.log(form); + $('exportErrorText').set('text', ''); $('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 selection = form.getElement('input[name=selector]:checked').get('value'); + var exportReq = new Request.JSON({url: thisUrl, method: 'post', link: 'cancel', onSuccess: exportResponse, onFailure: exportFail}); + var selector = form.querySelectorAll('input[name=selector]:checked'); + if ( !selector.length ) { + alert("Please select how to filter logs"); + return; + } + var selection = selector[0].get('value'); if ( selection == 'filter' || selection == 'current' ) { $$('#filters select').each( function( select ) { @@ -245,8 +271,8 @@ function exportRequest() { exportParms += "&maxTime="+encodeURIComponent(maxTime); } } - exportReq.send( exportParms+"&"+form.toQueryString() ); - $('exportLog').spin(); + exportReq.send(exportParms+"&"+form.toQueryString()); + $('log_exportModal').spin(); } } @@ -254,9 +280,9 @@ 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 ); + window.console.log('No selector found for ' + key); } return; } @@ -283,6 +309,8 @@ function updateFilterSelectors() { if ( filter[key] ) { selector.set('value', filter[key]); } + $j(selector).chosen('destroy'); + $j(selector).chosen(); } ); } @@ -300,12 +328,12 @@ function initPage() { } ); logTable.addEvent( 'sort', function( tbody, index ) { - var header = tbody.getParent( 'table' ).getElement( 'thead' ); - var columns = header.getElement( 'tr' ).getElements( 'th' ); + var header = tbody.getParent('table').getElement('thead'); + var columns = header.getElement('tr').getElements('th'); var column = columns[index]; - sortReversed = column.hasClass( 'table-th-sort-rev' ); + sortReversed = column.hasClass('table-th-sort-rev'); if ( logCount > displayLimit ) { - var rows = tbody.getElements( 'tr' ); + var rows = tbody.getElements('tr'); var startIndex; if ( sortReversed ) { startIndex = displayLimit; @@ -320,14 +348,9 @@ function initPage() { } // end if loCount > displayLimit } ); - exportFormValidator = new Form.Validator.Inline($('exportForm'), { - useTitles: true, - warningPrefix: "", - errorPrefix: "" - }); - new Asset.css( "css/spinner.css" ); + new Asset.css('css/spinner.css'); fetchNextLogs(); } // Kick everything off -window.addEventListener( 'DOMContentLoaded', initPage ); +window.addEventListener('DOMContentLoaded', initPage); diff --git a/web/skins/classic/views/js/login.js b/web/skins/classic/views/js/login.js index 3adb244b1..5142ce310 100644 --- a/web/skins/classic/views/js/login.js +++ b/web/skins/classic/views/js/login.js @@ -1,5 +1,5 @@ window.addEventListener( 'DOMContentLoaded', function() { if ( failed == true ) { - $('loginError').removeClass( 'hidden' ); + $j('#loginError').show(); } } ); diff --git a/web/skins/classic/views/js/monitor.js b/web/skins/classic/views/js/monitor.js index bf5e4d900..0bedf5303 100644 --- a/web/skins/classic/views/js/monitor.js +++ b/web/skins/classic/views/js/monitor.js @@ -1,29 +1,54 @@ -function updateMonitorDimensions( element ) { +function updateMonitorDimensions(element) { var form = element.form; - var widthFactor = parseInt( defaultAspectRatio.replace( /:.*$/, '' ) ); - var heightFactor = parseInt( defaultAspectRatio.replace( /^.*:/, '' ) ); + if ( element.type == 'number' ) { + // either width or height + + var widthFactor = parseInt(defaultAspectRatio.replace(/:.*$/, '')); + var heightFactor = parseInt(defaultAspectRatio.replace(/^.*:/, '')); - if ( form.elements['preserveAspectRatio'].checked ) { var monitorWidth = parseInt(form.elements['newMonitor[Width]'].value); var monitorHeight = parseInt(form.elements['newMonitor[Height]'].value); - switch ( element.name ) { - case 'newMonitor[Width]': - if ( monitorWidth >= 0 ) { - form.elements['newMonitor[Height]'].value = Math.round((monitorWidth * heightFactor) / widthFactor); - } else { - form.elements['newMonitor[Height]'].value = ''; - } - break; - case 'newMonitor[Height]': - if ( monitorHeight >= 0 ) { - form.elements['newMonitor[Width]'].value = Math.round((monitorHeight * widthFactor) / heightFactor); - } else { - form.elements['newMonitor[Width]'].value = ''; - } - break; + + if ( form.elements['preserveAspectRatio'].checked ) { + switch ( element.name ) { + case 'newMonitor[Width]': + if ( monitorWidth >= 0 ) { + form.elements['newMonitor[Height]'].value = Math.round((monitorWidth * heightFactor) / widthFactor); + } else { + form.elements['newMonitor[Height]'].value = ''; + } + monitorHeight = parseInt(form.elements['newMonitor[Height]'].value); + break; + case 'newMonitor[Height]': + if ( monitorHeight >= 0 ) { + form.elements['newMonitor[Width]'].value = Math.round((monitorHeight * widthFactor) / heightFactor); + } else { + form.elements['newMonitor[Width]'].value = ''; + } + monitorWidth = parseInt(form.elements['newMonitor[Width]'].value); + break; + } + } + // If we find a matching option in the dropdown, select it or select custom + + var option = $j('select[name="dimensions_select"] option[value="'+monitorWidth+'x'+monitorHeight+'"]'); + if ( !option.size() ) { + $j('select[name="dimensions_select"]').val(''); + } else { + $j('select[name="dimensions_select"]').val(monitorWidth+'x'+monitorHeight); + } + } else { + // For some reason we get passed the first option instead of the select + element = form.elements['dimensions_select']; + + var value = element.options[element.selectedIndex].value; + if ( value != '' ) { // custom dimensions + var dimensions = value.split('x'); + form.elements['newMonitor[Width]'].value = dimensions[0]; + form.elements['newMonitor[Height]'].value = dimensions[1]; } } - return ( false ); + return false; } function loadLocations( element ) { @@ -43,6 +68,9 @@ function loadLocations( element ) { } function initPage() { + var backBtn = $j('#backBtn'); + var onvifBtn = $j('#onvifBtn'); + //var protocolSelector = $('contentForm').elements['newMonitor[Protocol]']; //if ( $(protocolSelector).getTag() == 'select' ) //updateMethods( $(protocolSelector) ); @@ -94,8 +122,124 @@ function initPage() { } }; }); + document.querySelectorAll('input[name="newMonitor[Width]"]').forEach(function(el) { + el.oninput = window['updateMonitorDimensions'].bind(el, el); + }); + document.querySelectorAll('input[name="newMonitor[Height]"]').forEach(function(el) { + el.oninput = window['updateMonitorDimensions'].bind(el, el); + }); + document.querySelectorAll('select[name="dimensions_select"]').forEach(function(el) { + el.onchange = window['updateMonitorDimensions'].bind(el, el); + }); + 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); + }); + document.querySelectorAll('select[name="newMonitor[Type]"]').forEach(function(el) { + el.onchange = function() { + var form = document.getElementById('contentForm'); + form.tab.value = 'general'; + form.submit(); + }; + }); + document.querySelectorAll('input[name="newMonitor[ImageBufferCount]"],input[name="newMonitor[Width]"],input[name="newMonitor[Height]"]').forEach(function(el) { + el.oninput = window['update_estimated_ram_use'].bind(el); + }); $j('.chosen').chosen(); + + // Don't enable the back button if there is no previous zm page to go back to + backBtn.prop('disabled', !document.referrer.length); + + // Manage the BACK button + document.getElementById("backBtn").addEventListener("click", function onBackClick(evt) { + evt.preventDefault(); + window.history.back(); + }); + + // Manage the REFRESH Button + document.getElementById("refreshBtn").addEventListener("click", function onRefreshClick(evt) { + evt.preventDefault(); + window.location.reload(true); + }); + + // Manage the PROBE button + $j('#probeBtn').click(function(evt) { + var mid = evt.currentTarget.getAttribute("data-mid"); + evt.preventDefault(); + + //FIX-ME: MAKE THIS A MODAL + //$j('#modalFunction-'+mid).modal('show'); + window.location.assign('?view=monitorprobe&mid='+mid); + }); + + // Manage the ONVIF button + $j('#onvifBtn').click(function(evt) { + var mid = evt.currentTarget.getAttribute("data-mid"); + evt.preventDefault(); + + //FIX-ME: MAKE THIS A MODAL + //$j('#modalFunction-'+mid).modal('show'); + window.location.assign('?view=onvifprobe&mid='+mid); + }); + + // Don't enable the onvif button if there is no previous zm page to go back to + onvifBtn.prop('disabled', !hasOnvif); + + // Manage the PRESET button + $j('#presetBtn').click(function(evt) { + var mid = evt.currentTarget.getAttribute("data-mid"); + evt.preventDefault(); + + //FIX-ME: MAKE THIS A MODAL + //$j('#modalFunction-'+mid).modal('show'); + window.location.assign('?view=monitorpreset&mid='+mid); + }); + + // Manage the CANCEL Button + document.getElementById("cancelBtn").addEventListener("click", function onCancelClick(evt) { + evt.preventDefault(); + window.location.assign('?view=console'); + }); } // 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 + ); +} + +function update_estimated_ram_use() { + var buffer_count = document.querySelectorAll('input[name="newMonitor[ImageBufferCount]"]')[0].value; + console.log(buffer_count); + var width = document.querySelectorAll('input[name="newMonitor[Width]"]')[0].value; + console.log(width); + var height = document.querySelectorAll('input[name="newMonitor[Height]"]')[0].value; + console.log(height); + var colours = document.querySelectorAll('select[name="newMonitor[Colours]"]')[0].value; + console.log(colours); + + document.getElementById('estimated_ram_use').innerHTML = human_filesize(buffer_count * width * height * colours, 0); +} + window.addEventListener('DOMContentLoaded', initPage); diff --git a/web/skins/classic/views/js/monitor.js.php b/web/skins/classic/views/js/monitor.js.php index 6bb6f73ff..b59049f12 100644 --- a/web/skins/classic/views/js/monitor.js.php +++ b/web/skins/classic/views/js/monitor.js.php @@ -1,4 +1,5 @@ var optControl = ; +var hasOnvif = ; var defaultAspectRatio = ''; var controlOptions = new Object(); translate('None') ); - # Temporary workaround to show all ptz control types regardless of monitor source type - # $sql = "select * from Controls where Type = '".$newMonitor['Type']."'"; - $sql = "select * from Controls"; - foreach( dbFetchAll( $sql ) as $row ) { - $controlTypes[$row['Id']] = $row['Name']; -?> -controlOptions[] = new Array(); - -controlOptions[][0] = ''; - -controlOptions[][0] = null; -Id().'] = new Array(); +controlOptions['.$control->Id().'][0] = '. + ( $control->HasHomePreset() ? '\''.translate('Home').'\'' : 'null' ).PHP_EOL; + for ( $i = 1; $i <= $control->NumPresets(); $i++ ) { + echo 'controlOptions['. $control->Id().']['.$i.'] = \''.translate('Preset').' '.$i .'\';'.PHP_EOL; } - for ( $i = 1; $i <= $row['NumPresets']; $i++ ) { -?> -controlOptions[][] = ''; - var monitorNames = new Object(); -monitorNames[''] = true; - @@ -70,6 +56,11 @@ function validateForm( form ) { errors[errors.length] = ""; //if ( !form.elements['newMonitor[Path]'].value ) //errors[errors.length] = ""; + } else if ( form.elements['newMonitor[Type]'].value == 'Ffmpeg' ) { + if ( !form.elements['newMonitor[Path]'].value ) +//|| !form.elements['newMonitor[Path]'].value.match( /^\d+$/ ) ) // valid url + errors[errors.length] = ""; + } else if ( form.elements['newMonitor[Type]'].value == 'File' ) { if ( !form.elements['newMonitor[Path]'].value ) errors[errors.length] = ""; @@ -132,22 +123,26 @@ function validateForm( form ) { } if ( errors.length ) { - alert( errors.join( "\n" ) ); + alert(errors.join("\n")); return false; } + + var warnings = new Array(); + if ( (form.elements['newMonitor[Function]'].value != 'Monitor') && (form.elements['newMonitor[Function]'].value != 'None') ) { + if ( (form.elements['newMonitor[SaveJPEGs]'].value == '0') && (form.elements['newMonitor[VideoWriter]'].value == '0') ) { + warnings[warnings.length] = ""; + } + } + if ( warnings.length ) { + if ( !confirm(warnings.join("\n")) ) { + return false; + } + } + return true; } -function updateLinkedMonitors( element ) { - var form = element.form; - var monitorIds = new Array(); - for ( var i = 0; i < element.options.length; i++ ) - if ( element.options[i].selected ) - monitorIds[monitorIds.length] = element.options[i].value; - form.elements['newMonitor[LinkedMonitors]'].value = monitorIds.join( ',' ); -} - -function updateMethods( element ) { +function updateMethods(element) { var form = element.form; var origMethod = form.elements['origMethod']; @@ -155,31 +150,29 @@ function updateMethods( element ) { methodSelector.options.length = 0; switch ( element.value ) { case 'http' : - { - $label ) { - ?> - methodSelector.options[methodSelector.options.length] = new Option( "", "" ); - if ( origMethod.value == "" ) - methodSelector.selectedIndex = methodSelector.options.length-1; - + $label ) { + ?> + methodSelector.options[methodSelector.options.length] = new Option("", ""); + if ( origMethod.value == "" ) + methodSelector.selectedIndex = methodSelector.options.length-1; + break; - } case 'rtsp' : - { - $label ) { - ?> - methodSelector.options[methodSelector.options.length] = new Option( "", "" ); - if ( origMethod.value == "" ) - methodSelector.selectedIndex = form.elements['newMonitor[Method]'].options.length-1; - - break; - } + $label ) { + ?> + methodSelector.options[methodSelector.options.length] = new Option( "", "" ); + if ( origMethod.value == "" ) + methodSelector.selectedIndex = form.elements['newMonitor[Method]'].options.length-1; + + break; } - return( true ); + return true; } diff --git a/web/skins/classic/views/js/montage.js b/web/skins/classic/views/js/montage.js index 1599e0dd7..03fc5c3f8 100644 --- a/web/skins/classic/views/js/montage.js +++ b/web/skins/classic/views/js/montage.js @@ -2,228 +2,25 @@ var requestQueue = new Request.Queue({ concurrent: monitorData.length, stopOnFailure: false }); - -function Monitor(monitorData) { - this.id = monitorData.id; - this.connKey = monitorData.connKey; - this.url = monitorData.url; - this.status = null; - this.alarmState = STATE_IDLE; - this.lastAlarmState = STATE_IDLE; - this.streamCmdParms = 'view=request&request=stream&connkey='+this.connKey; - if ( auth_hash ) { - this.streamCmdParms += '&auth='+auth_hash; - } - this.streamCmdTimer = null; - this.type = monitorData.type; - this.refresh = monitorData.refresh; - this.start = function(delay) { - if ( this.streamCmdQuery ) { - this.streamCmdTimer = this.streamCmdQuery.delay(delay, this); - } else { - console.log("No streamCmdQuery"); - } - }; - - this.eventHandler = function( event ) { - console.log(event); - }; - - this.onclick = function(evt) { - var el = evt.currentTarget; - var tag = 'watch'; - var id = el.getAttribute("data-monitor-id"); - var width = el.getAttribute("data-width"); - var height = el.getAttribute("data-height"); - var url = '?view=watch&mid='+id; - var name = 'zmWatch'+id; - evt.preventDefault(); - createPopup(url, name, tag, width, height); - }; - - this.setup_onclick = function() { - var el = document.getElementById('imageFeed'+this.id); - if ( el ) el.addEventListener('click', this.onclick, false); - }; - this.disable_onclick = function() { - document.getElementById('imageFeed'+this.id).removeEventListener('click', this.onclick ); - }; - - this.setStateClass = function(element, stateClass) { - if ( !element.hasClass( stateClass ) ) { - if ( stateClass != 'alarm' ) { - element.removeClass('alarm'); - } - if ( stateClass != 'alert' ) { - element.removeClass('alert'); - } - if ( stateClass != 'idle' ) { - element.removeClass('idle'); - } - element.addClass(stateClass); - } - }; - - this.onError = function(text, error) { - console.log('onerror: ' + text + ' error:'+error); - // Requeue, but want to wait a while. - var streamCmdTimeout = 10*statusRefreshTimeout; - this.streamCmdTimer = this.streamCmdQuery.delay(streamCmdTimeout, this); - }; - this.onFailure = function(xhr) { - console.log('onFailure: ' + this.connKey); - console.log(xhr); - if ( ! requestQueue.hasNext("cmdReq"+this.id) ) { - console.log("Not requeuing because there is one already"); - requestQueue.addRequest("cmdReq"+this.id, this.streamCmdReq); - } - if ( 0 ) { - // Requeue, but want to wait a while. - if ( this.streamCmdTimer ) { - this.streamCmdTimer = clearTimeout( this.streamCmdTimer ); - } - var streamCmdTimeout = 1000*statusRefreshTimeout; - this.streamCmdTimer = this.streamCmdQuery.delay( streamCmdTimeout, this, true ); - requestQueue.resume(); - } - console.log("done failure"); - }; - - this.getStreamCmdResponse = function(respObj, respText) { - if ( this.streamCmdTimer ) { - this.streamCmdTimer = clearTimeout( this.streamCmdTimer ); - } - - var stream = $j('#liveStream'+this.id)[0]; - - if ( respObj.result == 'Ok' ) { - if ( respObj.status ) { - this.status = respObj.status; - this.alarmState = this.status.state; - - var stateClass = ""; - if ( this.alarmState == STATE_ALARM ) { - stateClass = "alarm"; - } else if ( this.alarmState == STATE_ALERT ) { - stateClass = "alert"; - } else { - stateClass = "idle"; - } - - if ( (!COMPACT_MONTAGE) && (this.type != 'WebSite') ) { - $('fpsValue'+this.id).set('text', this.status.fps); - $('stateValue'+this.id).set('text', stateStrings[this.alarmState]); - this.setStateClass($('monitorState'+this.id), stateClass); - } - this.setStateClass($('monitor'+this.id), stateClass); - - /*Stream could be an applet so can't use moo tools*/ - stream.className = stateClass; - - var isAlarmed = ( this.alarmState == STATE_ALARM || this.alarmState == STATE_ALERT ); - var wasAlarmed = ( this.lastAlarmState == STATE_ALARM || this.lastAlarmState == STATE_ALERT ); - - var newAlarm = ( isAlarmed && !wasAlarmed ); - var oldAlarm = ( !isAlarmed && wasAlarmed ); - - if ( newAlarm ) { - if ( false && SOUND_ON_ALARM ) { - // Enable the alarm sound - $('alarmSound').removeClass('hidden'); - } - if ( POPUP_ON_ALARM ) { - windowToFront(); - } - } - if ( false && SOUND_ON_ALARM ) { - if ( oldAlarm ) { - // Disable alarm sound - $('alarmSound').addClass('hidden'); - } - } - if ( this.status.auth ) { - if ( this.status.auth != auth_hash ) { - // Try to reload the image stream. - if ( stream ) { - stream.src = stream.src.replace(/auth=\w+/i, 'auth='+this.status.auth); - } - console.log("Changed auth from " + auth_hash + " to " + this.status.auth); - auth_hash = this.status.auth; - } - } // end if have a new auth hash - } // end if has state - } else { - console.error(respObj.message); - // Try to reload the image stream. - if ( stream ) { - if ( stream.src ) { - console.log('Reloading stream: ' + stream.src); - stream.src = stream.src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) )); - } else { - } - } else { - console.log('No stream to reload?'); - } - } // end if Ok or not - - var streamCmdTimeout = statusRefreshTimeout; - // The idea here is if we are alarmed, do updates faster. - // However, there is a timeout in the php side which isn't getting modified, - // so this may cause a problem. Also the server may only be able to update so fast. - //if ( this.alarmState == STATE_ALARM || this.alarmState == STATE_ALERT ) { - //streamCmdTimeout = streamCmdTimeout/5; - //} - this.streamCmdTimer = this.streamCmdQuery.delay(streamCmdTimeout, this); - this.lastAlarmState = this.alarmState; - }; - - this.streamCmdQuery = function(resent) { - if ( resent ) { - console.log(this.connKey+": timeout: Resending"); - this.streamCmdReq.cancel(); - } - //console.log("Starting CmdQuery for " + this.connKey ); - if ( this.type != 'WebSite' ) { - this.streamCmdReq.send(this.streamCmdParms+"&command="+CMD_QUERY); - } - }; - - if ( this.type != 'WebSite' ) { - this.streamCmdReq = new Request.JSON( { - url: this.url, - method: 'get', - timeout: AJAX_TIMEOUT, - onSuccess: this.getStreamCmdResponse.bind(this), - onTimeout: this.streamCmdQuery.bind(this, true), - onError: this.onError.bind(this), - onFailure: this.onFailure.bind(this), - link: 'cancel' - } ); - console.log("queueing for " + this.id + " " + this.connKey + " timeout is: " + AJAX_TIMEOUT); - requestQueue.addRequest("cmdReq"+this.id, this.streamCmdReq); - } -} // end function Monitor - /** * called when the layoutControl select element is changed, or the page * is rendered * @param {*} element - the event data passed by onchange callback */ function selectLayout(element) { - console.dir(element); - layout = $j(element).val(); + var ddm = $j('#zmMontageLayout'); + layout = ddm.val(); if ( layout_id = parseInt(layout) ) { layout = layouts[layout]; - console.log(layout); for ( var i = 0, length = monitors.length; i < length; i++ ) { monitor = monitors[i]; // Need to clear the current positioning, and apply the new monitor_frame = $j('#monitorFrame'+monitor.id); - if ( ! monitor_frame ) { - console.log("Error finding frame for " + monitor.id); + if ( !monitor_frame ) { + console.log('Error finding frame for ' + monitor.id); continue; } @@ -231,6 +28,7 @@ function selectLayout(element) { if ( layout.Positions['default'] ) { styles = layout.Positions['default']; for ( style in styles ) { + console.log("Applying " + style + ' ' + styles[style]); monitor_frame.css(style, styles[style]); } } else { @@ -241,7 +39,6 @@ function selectLayout(element) { styles = layout.Positions['mId'+monitor.id]; for ( style in styles ) { monitor_frame.css(style, styles[style]); - console.log("Applying " + style + ' : ' + styles[style]); } } else { console.log("No Monitor styles to apply"); @@ -253,35 +50,52 @@ function selectLayout(element) { } Cookie.write('zmMontageLayout', layout_id, {duration: 10*365}); if ( layouts[layout_id].Name != 'Freeform' ) { // 'montage_freeform.css' ) { - Cookie.write( 'zmMontageScale', '', {duration: 10*365} ); + Cookie.write('zmMontageScale', '', {duration: 10*365}); $('scale').set('value', ''); - $('width').set('value', ''); - for ( var i = 0, length = monitors.length; i < length; i++ ) { - var monitor = monitors[i]; - var streamImg = $('liveStream'+monitor.id); - if ( streamImg ) { - if ( streamImg.nodeName == 'IMG' ) { - var src = streamImg.src; - src = src.replace(/width=[\.\d]+/i, 'width=0' ); - if ( src != streamImg.src ) { - streamImg.src = ''; - streamImg.src = src; - } - } else if ( streamImg.nodeName == 'APPLET' || streamImg.nodeName == 'OBJECT' ) { - // APPLET's and OBJECTS need to be re-initialized - } - streamImg.style.width = '100%'; - } - } // end foreach monitor + $('width').set('value', '0'); + } else { + // Is freeform, we don't touch the width/height/scale settings, but we may need to update sizing and scales } + var width = parseInt($('width').get('value')); + var height = parseInt($('height').get('value')); + var scale = $('scale').get('value'); + + for ( var i = 0, length = monitors.length; i < length; i++ ) { + var monitor = monitors[i]; + + if ( scale ) { + stream_scale = scale; + } else if ( width ) { + stream_scale = parseInt(100*width/monitor.width); + } else if ( height ) { + stream_scale = parseInt(100*height/monitor.height); + } + var streamImg = $('liveStream'+monitor.id); + if ( streamImg ) { + if ( streamImg.nodeName == 'IMG' ) { + var src = streamImg.src; + src = src.replace(/scale=\d*/i, 'scale='+scale); + if ( height == '0' ) { + streamImg.style.height = 'auto'; + } + if ( src != streamImg.src ) { + streamImg.src = ''; + streamImg.src = src; + } + } else if ( streamImg.nodeName == 'APPLET' || streamImg.nodeName == 'OBJECT' ) { + // APPLET's and OBJECTS need to be re-initialized + } + streamImg.style.width = '100%'; + } + } // end foreach monitor } // end function selectLayout(element) /** * called when the widthControl|heightControl select elements are changed */ function changeSize() { - var width = $('width').get('value'); - var height = $('height').get('value'); + var width = parseInt($('width').get('value')); + var height = parseInt($('height').get('value')); for ( var i = 0, length = monitors.length; i < length; i++ ) { var monitor = monitors[i]; @@ -292,12 +106,8 @@ function changeSize() { console.log("Error finding frame for " + monitor.id); continue; } - if ( width ) { - monitor_frame.css('width', width); - } - if ( height ) { - monitor_frame.css('height', height); - } + monitor_frame.css('width', ( width ? width+'px' : 'auto')); + monitor_frame.css('height', ( height ? height+'px' : 'auto')); /*Stream could be an applet so can't use moo tools*/ var streamImg = $('liveStream'+monitor.id); @@ -305,13 +115,20 @@ function changeSize() { if ( streamImg.nodeName == 'IMG' ) { var src = streamImg.src; streamImg.src = ''; - src = src.replace(/width=[\.\d]+/i, 'width='+width); - src = src.replace(/height=[\.\d]+/i, 'height='+height); + var scale = 100; + if ( width ) { + scale = parseInt(100*width/monitor.width); + } else if ( height ) { + scale = parseInt(100*height/monitor.height); + } + // Note zms ignores width and height + src = src.replace(/scale=\d*/i, 'scale='+scale); + src = src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) )); streamImg.src = src; } - streamImg.style.width = width ? width : null; - streamImg.style.height = height ? height : null; + streamImg.style.width = width ? width+'px' : null; + streamImg.style.height = height ? height+'px' : null; //streamImg.style.height = ''; } } @@ -319,7 +136,8 @@ function changeSize() { Cookie.write('zmMontageScale', '', {duration: 10*365}); Cookie.write('zmMontageWidth', width, {duration: 10*365}); Cookie.write('zmMontageHeight', height, {duration: 10*365}); - selectLayout('#zmMontageLayout'); + jQuery("#zmMontageLayout option:selected").removeAttr("selected"); + //selectLayout('#zmMontageLayout'); } // end function changeSize() /** @@ -327,12 +145,12 @@ function changeSize() { */ function changeScale() { var scale = $('scale').get('value'); - $('width').set('value', 'auto'); - $('height').set('value', 'auto'); + $('width').set('value', '0'); //auto + $('height').set('value', '0'); //auto Cookie.write('zmMontageScale', scale, {duration: 10*365}); Cookie.write('zmMontageWidth', '', {duration: 10*365}); Cookie.write('zmMontageHeight', '', {duration: 10*365}); - if ( !scale ) { + if ( scale == '' ) { selectLayout('#zmMontageLayout'); return; } @@ -347,8 +165,12 @@ function changeScale() { console.log("Error finding frame for " + monitor.id); continue; } - if ( newWidth ) { - monitor_frame.css('width', newWidth); + if ( scale != '0' ) { + if ( newWidth ) { + monitor_frame.css('width', newWidth); + } + } else { + monitor_frame.css('width', '100%'); } // We don't set the frame height because it has the status bar as well //if ( height ) { @@ -362,15 +184,22 @@ function changeScale() { streamImg.src = ''; //src = src.replace(/rand=\d+/i,'rand='+Math.floor((Math.random() * 1000000) )); - src = src.replace(/scale=[\.\d]+/i, 'scale='+scale); - src = src.replace(/width=[\.\d]+/i, 'width='+newWidth); - src = src.replace(/height=[\.\d]+/i, 'height='+newHeight); + if ( scale != '0' ) { + src = src.replace(/scale=[\.\d]+/i, 'scale='+scale); + } else { + src = src.replace(/scale=[\.\d]+/i, 'scale=100'); + } streamImg.src = src; } - streamImg.style.width = newWidth + "px"; - streamImg.style.height = newHeight + "px"; - } - } + if ( scale != '0' ) { + streamImg.style.width = newWidth + 'px'; + streamImg.style.height = newHeight + 'px'; + } else { + streamImg.style.width = '100%'; + streamImg.style.height = 'auto'; + } + } // end if StreamImg + } // end foreach Monitor } function toGrid(value) { @@ -396,6 +225,17 @@ function edit_layout(button) { function save_layout(button) { var form = button.form; + var name = form.elements['Name'].value; + + if ( !name ) { + name = form.elements['zmMontageLayout'].options[form.elements['zmMontageLayout'].selectedIndex].text; + } + + if ( name=='Freeform' || name=='2 Wide' || name=='3 Wide' || name=='4 Wide' || name=='5 Wide' ) { + alert('You cannot edit the built in layouts. Please give the layout a new name.'); + return; + } + // In fixed positioning, order doesn't matter. In floating positioning, it does. var Positions = {}; for ( var i = 0, length = monitors.length; i < length; i++ ) { @@ -450,10 +290,11 @@ function initPage() { } for ( var i = 0, length = monitorData.length; i < length; i++ ) { - monitors[i] = new Monitor(monitorData[i]); + monitors[i] = new MonitorStream(monitorData[i]); // Start the fps and status updates. give a random delay so that we don't assault the server var delay = Math.round( (Math.random()+0.5)*statusRefreshTimeout ); + console.log("delay: " + delay); monitors[i].start(delay); var interval = monitors[i].refresh; diff --git a/web/skins/classic/views/js/montage.js.php b/web/skins/classic/views/js/montage.js.php index fe298ab10..5a2aef2ed 100644 --- a/web/skins/classic/views/js/montage.js.php +++ b/web/skins/classic/views/js/montage.js.php @@ -28,15 +28,16 @@ var canStreamNative = ; var monitorData = new Array(); monitorData[monitorData.length] = { 'id': Id() ?>, 'connKey': connKey() ?>, - 'width': Width() ?>, - 'height':Height() ?>, - 'url': 'UrlToIndex() ?>', - 'onclick': function(){createPopup( '?view=watch&mid=Id() ?>', 'zmWatchId() ?>', 'watch', Width(), $monitor->PopupScale() ); ?>, Height(), $monitor->PopupScale() ); ?> );}, + 'width': ViewWidth() ?>, + 'height':ViewHeight() ?>, + 'url': 'UrlToIndex( ZM_MIN_STREAMING_PORT ? ($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() ?>' }; @@ -46,9 +47,10 @@ monitorData[monitorData.length] = { layouts = new Array(); layouts[0] = {}; // reserved, should hold which fields to clear when transitioning -layouts[Id() ?>] = {"Name":"Name()?>","Positions":Positions() ?>}; +layouts[Id() ?>] = {"Name":"Name()?>","Positions":Positions())?$layout->Positions():'{}' ?>}; diff --git a/web/skins/classic/views/js/montagereview.js b/web/skins/classic/views/js/montagereview.js index f95268e5a..5207f24d5 100644 --- a/web/skins/classic/views/js/montagereview.js +++ b/web/skins/classic/views/js/montagereview.js @@ -48,8 +48,7 @@ function evaluateLoadTimes() { function getFrame(monId, time, last_Frame) { if ( last_Frame ) { if ( - (last_Frame.TimeStampSecs <= time) - && + (last_Frame.TimeStampSecs <= time) && (last_Frame.EndTimeStampSecs >= time) ) { return last_Frame; @@ -104,12 +103,11 @@ function getFrame(monId, time, last_Frame) { continue; } if ( - e.FramesById[frame_id].TimeStampSecs == time - || ( - e.FramesById[frame_id].TimeStampSecs < time - && ( - (!e.FramesById[frame_id].NextTimeStampSecs) // only if event.EndTime is null - || + e.FramesById[frame_id].TimeStampSecs == time || + ( + e.FramesById[frame_id].TimeStampSecs < time && + ( + (!e.FramesById[frame_id].NextTimeStampSecs) || // only if event.EndTime is null (e.FramesById[frame_id].NextTimeStampSecs > time) ) ) @@ -154,9 +152,11 @@ function getImageSource(monId, time) { var duration = Frame.NextTimeStampSecs - Frame.TimeStampSecs; frame_id = Frame.FrameId + parseInt( (NextFrame.FrameId-Frame.FrameId) * ( time-Frame.TimeStampSecs )/duration ); //console.log("Have NextFrame: duration: " + duration + " frame_id = " + frame_id + " from " + NextFrame.FrameId + ' - ' + Frame.FrameId + " time: " + (time-Frame.TimeStampSecs) ); + } else { + frame_id = Frame.FrameId; } } else { - frame_id = Frame['Id']; + frame_id = Frame.FrameId; console.log("No NextFrame"); } Event = events[Frame.EventId]; @@ -934,6 +934,10 @@ function changeDateTime(e) { } } + // Reloading can take a while, so stop interrupts to reduce load + clearInterval(timerObj); + timerObj = null; + var uri = "?view=" + currentView + fitStr + minStr + maxStr + liveStr + zoomStr + "&scale=" + $j("#scaleslider")[0].value + "&speed=" + speeds[$j("#speedslider")[0].value]; window.location = uri; } @@ -965,6 +969,7 @@ function initPage() { imagedone(this, this.monId, false); }; loadImage2Monitor(monId, monitorImageURL[monId]); + monitorCanvasObj[monId].addEventListener('click', clickMonitor, false); } } // end foreach monitor @@ -980,15 +985,6 @@ function initPage() { ctx = canvas.getContext('2d'); drawGraph(); } - for ( i=0, len=monitorPtr.length; i < len; i += 1 ) { - var monitor_id = monitorPtr[i]; - monitor_canvas = $('Monitor'+monitor_id); - if ( ! monitor_canvas ) { - console.log("No canvas found for monitor " + monitor_id); - continue; - } - monitor_canvas.addEventListener('click', clickMonitor, false); - } setSpeed(speedIndex); //setFit(fitMode); // will redraw //setLive(liveMode); // will redraw @@ -1007,7 +1003,7 @@ function initPage() { $j('#maxTime').datetimepicker({ timeFormat: "HH:mm:ss", dateFormat: "yy-mm-dd", - minDate: $j('#minTime').val(), + minDate: minTime, maxDate: +0, constrainInput: false, onClose: function(newDate, oldData) { diff --git a/web/skins/classic/views/js/montagereview.js.php b/web/skins/classic/views/js/montagereview.js.php index 4fc8ef511..d2277b507 100644 --- a/web/skins/classic/views/js/montagereview.js.php +++ b/web/skins/classic/views/js/montagereview.js.php @@ -3,7 +3,21 @@ var server_utc_offset = getOffset($now); -echo $offset . '; // ' . floor($offset / 3600) . ' hours '; +echo $offset.'; // '.floor($offset / 3600).' hours '; + +global $defaultScale; +global $liveMode; +global $fitMode; +global $speeds; +global $speedIndex; +global $initialDisplayInterval; +global $minTimeSecs; +global $maxTimeSecs; +global $minTime; +global $maxTime; +global $monitors; +global $eventsSql; +global $framesSql; ?> var currentScale=; @@ -18,7 +32,7 @@ var speedIndex=; // for history, and fps for live, and dynamically determined (in ms) var currentDisplayInterval=; -var playSecsperInterval=1; // How many seconds of recorded image we play per refresh determined by speed (replay rate) and display interval; (default=1 if coming from live) +var playSecsPerInterval=1; // How many seconds of recorded image we play per refresh determined by speed (replay rate) and display interval; (default=1 if coming from live) var timerInterval; // milliseconds between interrupts var timerObj; // object to hold timer interval; var freeTimeLastIntervals=[]; // Percentage of current interval used in loading most recent image @@ -40,7 +54,7 @@ $maxScore = 0; if ( !$liveMode ) { $result = dbQuery($eventsSql); if ( !$result ) { - Fatal('SQL-ERR'); + ZM\Fatal('SQL-ERR'); return; } @@ -50,7 +64,6 @@ if ( !$liveMode ) { $event_id = $event['Id']; $EventsById[$event_id] = $event; } - $next_frames = array(); if ( $result = dbQuery($framesSql) ) { @@ -69,9 +82,9 @@ if ( !$liveMode ) { $frame['NextFrameId'] = $next_frames[$frame['EventId']]['Id']; } $event['FramesById'] += array($frame['Id']=>$frame); - $next_frames[$frame['EventId']] = $frame; + $next_frames[$frame['EventId']] = &$event['FramesById'][$frame['Id']]; } - } + } // end if dbQuery $events_by_monitor_id = array(); @@ -147,7 +160,6 @@ foreach ( ZM\Server::find() as $Server ) { echo 'Servers[' . $Server->Id() . '] = new Server(' . $Server->to_json(). ");\n"; } - echo ' var monitorName = []; var monitorLoading = []; @@ -168,8 +180,8 @@ var monitorCanvasCtx = []; var monitorPtr = []; // monitorName[monitorPtr[0]] is first monitor '; -$numMonitors=0; // this array is indexed by the monitor ID for faster access later, so it may be sparse -$avgArea=floatval(0); // Calculations the normalizing scale +$numMonitors = 0; // this array is indexed by the monitor ID for faster access later, so it may be sparse +$avgArea = floatval(0); // Calculations the normalizing scale foreach ( $monitors as $m ) { $avgArea = $avgArea + floatval($m->Width() * $m->Height()); @@ -183,18 +195,18 @@ foreach ( $monitors as $m ) { echo " monitorLoading[" . $m->Id() . "]=false;\n"; echo " monitorImageURL[" . $m->Id() . "]='".$m->getStreamSrc( array('mode'=>'single','scale'=>$defaultScale*100), '&' )."';\n"; echo " monitorLoadingStageURL[" . $m->Id() . "] = '';\n"; - echo " monitorColour[" . $m->Id() . "]=\"" . $m->WebColour() . "\";\n"; - echo " monitorWidth[" . $m->Id() . "]=" . $m->Width() . ";\n"; - echo " monitorHeight[" . $m->Id() . "]=" . $m->Height() . ";\n"; + echo " monitorColour[" . $m->Id() . "]=\"" . validHtmlStr($m->WebColour()) . "\";\n"; + echo " monitorWidth[" . $m->Id() . "]=" . validHtmlStr($m->ViewWidth()) . ";\n"; + echo " monitorHeight[" . $m->Id() . "]=" . validHtmlStr($m->ViewHeight()) . ";\n"; echo " monitorIndex[" . $m->Id() . "]=" . $numMonitors . ";\n"; echo " monitorServerId[" . $m->Id() . "]='" .($m->ServerId() ? $m->ServerId() : '0'). "';\n"; - echo " monitorName[" . $m->Id() . "]=\"" . $m->Name() . "\";\n"; + echo " monitorName[" . $m->Id() . "]=\"" . validHtmlStr($m->Name()) . "\";\n"; echo " monitorLoadStartTimems[" . $m->Id() . "]=0;\n"; echo " monitorLoadEndTimems[" . $m->Id() . "]=0;\n"; echo " monitorNormalizeScale[" . $m->Id() . "]=" . sqrt($avgArea / ($m->Width() * $m->Height() )) . ";\n"; $zoomScale=1.0; - if(isset($_REQUEST[ 'z' . $m->Id() ]) ) - $zoomScale = floatval( validHtmlStr($_REQUEST[ 'z' . $m->Id() ]) ); + if ( isset($_REQUEST['z'.$m->Id()]) ) + $zoomScale = floatval(validHtmlStr($_REQUEST['z'.$m->Id()])); echo " monitorZoomScale[" . $m->Id() . "]=" . $zoomScale . ";\n"; echo " monitorPtr[" . $numMonitors . "]=" . $m->Id() . ";\n"; $numMonitors += 1; @@ -203,15 +215,17 @@ echo " var numMonitors = $numMonitors; var minTimeSecs=parseInt($minTimeSecs); var maxTimeSecs=parseInt($maxTimeSecs); +var minTime='$minTime'; +var maxTime='$maxTime'; "; -echo "var rangeTimeSecs=" . ( $maxTimeSecs - $minTimeSecs + 1) . ";\n"; -if(isset($defaultCurrentTime)) - echo "var currentTimeSecs=parseInt(" . strtotime($defaultCurrentTime) . ");\n"; +echo 'var rangeTimeSecs='.($maxTimeSecs - $minTimeSecs + 1).";\n"; +if ( isset($defaultCurrentTime) ) + echo 'var currentTimeSecs=parseInt('.strtotime($defaultCurrentTime).");\n"; else - echo "var currentTimeSecs=parseInt(" . ($minTimeSecs + $maxTimeSecs)/2 . ");\n"; + echo 'var currentTimeSecs=parseInt('.(($minTimeSecs + $maxTimeSecs)/2).");\n"; echo 'var speeds=['; -for ($i=0; $i0)?', ':'') . $speeds[$i]; echo "];\n"; ?> diff --git a/web/skins/classic/views/js/newlog.js b/web/skins/classic/views/js/newlog.js new file mode 100644 index 000000000..f0b827912 --- /dev/null +++ b/web/skins/classic/views/js/newlog.js @@ -0,0 +1,101 @@ +var table = $j('#logTable'); + +/* +This is the format of the json object sent by bootstrap-table + +var params = +{ +"type":"get", +"data": + { + "search":"some search text", + "sort":"DateTime", + "order":"asc", + "offset":0, + "limit":25 + "filter": + { + "message":"some advanced search text" + "level":"some more advanced search text" + } + }, +"cache":true, +"contentType":"application/json", +"dataType":"json" +}; +*/ + +// Called by bootstrap-table to retrieve zm log data +function ajaxRequest(params) { + $j.getJSON(thisUrl + '?view=request&request=newlog&task=query', params.data) + .done(function(data) { + //console.log('Ajax parameters: ' + JSON.stringify(params)); + // rearrange the result into what bootstrap-table expects + params.success({total: data.total, totalNotFiltered: data.totalNotFiltered, rows: data.rows}); + updateHeaderStats(data); + }) + .fail(logAjaxFail); +} + +function updateHeaderStats(data) { + var pageNum = table.bootstrapTable('getOptions').pageNumber; + var pageSize = table.bootstrapTable('getOptions').pageSize; + var startRow = ( (pageNum - 1 ) * pageSize ) + 1; + var stopRow = pageNum * pageSize; + var newClass = (data.logstate == 'ok') ? 'text-success' : (data.logstate == 'alert' ? 'text-warning' : ((data.logstate == 'alarm' ? 'text-danger' : ''))); + + $j('#logState').text(data.logstate); + $j('#logState').removeClass('text-success'); + $j('#logState').removeClass('text-warning'); + $j('#logState').removeClass('text-danger'); + $j('#logState').addClass(newClass); + + $j('#totalLogs').text(data.total); + $j('#availLogs').text(data.totalNotFiltered); + $j('#lastUpdate').text(data.updated); + $j('#displayLogs').text(startRow + ' to ' + stopRow); +} + +function initPage() { + var backBtn = $j('#backBtn'); + + // Init the bootstrap-table with custom icons + table.bootstrapTable({icons: icons}); + + // Assign inf, err, fat, dbg color classes to the rows in the table + table.on('post-body.bs.table', function(data) { + $j('#logTable tr').each(function(ndx, row) { + var row = $j(row); + var level = row.find('td:eq(4)').text(); + + if (( level == 'FAT' ) || ( level == 'PNC' )) { + row.addClass('log-fat'); + } else if ( level == 'ERR' ) { + row.addClass('log-err'); + } else if ( level == 'WAR' ) { + row.addClass('log-war'); + } else if ( level == 'DBG' ) { + row.addClass('log-dbg'); + } + }); + }); + + // Don't enable the back button if there is no previous zm page to go back to + backBtn.prop('disabled', !document.referrer.length); + + // Manage the BACK button + document.getElementById("backBtn").addEventListener("click", function onBackClick(evt) { + evt.preventDefault(); + window.history.back(); + }); + + // Manage the REFRESH Button + document.getElementById("refreshBtn").addEventListener("click", function onRefreshClick(evt) { + evt.preventDefault(); + window.location.reload(true); + }); +} + +$j(document).ready(function() { + initPage(); +}); diff --git a/web/skins/classic/views/js/onvifprobe.js b/web/skins/classic/views/js/onvifprobe.js index b49ac5754..4f10e5c29 100644 --- a/web/skins/classic/views/js/onvifprobe.js +++ b/web/skins/classic/views/js/onvifprobe.js @@ -1,6 +1,6 @@ function submitCamera( element ) { var form = element.form; - form.target = opener.name; + form.target = self.name; form.view.value = 'monitor'; form.submit(); closeWindow.delay( 250 ); @@ -22,14 +22,18 @@ function gotoStep2( element ) { form.submit(); } -function configureButtons( element ) { +function configureButtons(element) { var form = element.form; - if (form.elements.namedItem("nextBtn")) { + if (form.elements.namedItem('nextBtn')) { form.nextBtn.disabled = (form.probe.selectedIndex==0) || - (form.username == "") || (form.username == null) || - (form.password == "") || (form.password == null); + (form.Username == '') || (form.Username == null) || + (form.Password == '') || (form.Password == null); } - if (form.elements.namedItem("saveBtn")) { + if (form.elements.namedItem('saveBtn')) { form.saveBtn.disabled = (form.probe.selectedIndex==0); } } + +function changeInterface(element) { + gotoStep1(element); +} diff --git a/web/skins/classic/views/js/options.js b/web/skins/classic/views/js/options.js new file mode 100644 index 000000000..843225a67 --- /dev/null +++ b/web/skins/classic/views/js/options.js @@ -0,0 +1,76 @@ +// Load the Server Modal HTML via Ajax call +function getServerModal(sid) { + $j.getJSON(thisUrl + '?request=modal&modal=server&id=' + sid) + .done(function(data) { + if ( $j('#ServerModal').length ) { + $j('#ServerModal').replaceWith(data.html); + } else { + $j("body").append(data.html); + } + $j('#ServerModal').modal('show'); + // Manage the Save button + $j('#serverSubmitBtn').click(function(evt) { + evt.preventDefault(); + $j('#serverModalForm').submit(); + }); + }) + .fail(logAjaxFail); +} + +function enableServerModal() { + $j(".serverCol").click(function(evt) { + evt.preventDefault(); + var sid = $j(this).data('sid'); + getServerModal(sid); + }); + $j('#NewServerBtn').click(function(evt) { + evt.preventDefault(); + getServerModal(0); + }); +} + +// Load the Storage Modal HTML via Ajax call +function getStorageModal(sid) { + $j.getJSON(thisUrl + '?request=modal&modal=storage&id=' + sid) + .done(function(data) { + if ( $j('#storageModal').length ) { + $j('#storageModal').replaceWith(data.html); + } else { + $j("body").append(data.html); + } + $j('#storageModal').modal('show'); + // Manage the Save button + $j('#storageSubmitBtn').click(function(evt) { + evt.preventDefault(); + $j('#storageModalForm').submit(); + }); + }) + .fail(logAjaxFail); +} + +function enableStorageModal() { + $j(".storageCol").click(function(evt) { + evt.preventDefault(); + var sid = $j(this).data('sid'); + getStorageModal(sid); + }); + $j('#NewStorageBtn').click(function(evt) { + evt.preventDefault(); + getStorageModal(0); + }); +} + +function initPage() { + var NewStorageBtn = $j('#NewStorageBtn'); + var NewServerBtn = $j('#NewServerBtn'); + + if ( canEditSystem ) enableStorageModal(); + if ( canEditSystem ) enableServerModal(); + + NewStorageBtn.prop('disabled', !canEditSystem); + NewServerBtn.prop('disabled', !canEditSystem); +} + +$j(document).ready(function() { + initPage(); +}); diff --git a/web/skins/classic/views/js/options.js.php b/web/skins/classic/views/js/options.js.php index 88387134a..5281cde8c 100644 --- a/web/skins/classic/views/js/options.js.php +++ b/web/skins/classic/views/js/options.js.php @@ -1,4 +1,7 @@ + var restartWarning = ; if ( restartWarning ) { alert( "" ); } + +var canEditSystem = ; diff --git a/web/skins/classic/views/js/postlogin.js.php b/web/skins/classic/views/js/postlogin.js.php index 6fa681fad..b5d712477 100644 --- a/web/skins/classic/views/js/postlogin.js.php +++ b/web/skins/classic/views/js/postlogin.js.php @@ -6,25 +6,29 @@ // will save the GET request via the postLoginQuery variable. After logging in, this // view receives the postLoginQuery via the login form submission, and we can then // redirect the user to his original intended destination by appending it to the URL. +// ?> -( +$j( function () { // Append '?(GET query)' to URL if the GET query is not empty. - var querySuffix = ""; + ?>'; - if ( querySuffix == '?view=login' ) { + if ( querySuffix == '?view=login' || querySuffix == '' ) { // If we didn't redirect elsewhere, then don't show login page, go to console querySuffix = '?view=console'; } var newUrl = querySuffix; - -console.log("Redirecting to" + newUrl + ' ' + thisUrl); +console.log("Current location: " + window.location); +console.log("Redirecting to (" + newUrl + ') from :' + thisUrl); window.location.replace(newUrl); } ).delay( 500 ); diff --git a/web/skins/classic/views/js/state.js b/web/skins/classic/views/js/state.js deleted file mode 100644 index 7165c690c..000000000 --- a/web/skins/classic/views/js/state.js +++ /dev/null @@ -1,62 +0,0 @@ -$j(document).ready(function() { - // Enable or disable the Delete button depending on the selected run state - $j("#runState").change(function() { - runstate = $j(this).val(); - - if ( (runstate == 'stop') || (runstate == 'restart') || (runstate == 'start') || (runstate == 'default') ) { - $j("#btnDelete").prop("disabled", true); - } else { - $j("#btnDelete").prop("disabled", false); - } - }); - - // Enable or disable the Save button when entering a new state - $j("#newState").keyup(function() { - length = $j(this).val().length; - if ( length < 1 ) { - $j("#btnSave").prop("disabled", true); - } else { - $j("#btnSave").prop("disabled", false); - } - }); - - - // Delete a state - $j("#btnDelete").click(function() { - stateStuff('delete', $j("#runState").val()); - }); - - - // Save a new state - $j("#btnSave").click(function() { - stateStuff('save', undefined, $j("#newState").val()); - }); - - // Change state - $j("#btnApply").click(function() { - stateStuff('state', $j("#runState").val()); - }); - - function stateStuff(action, runState, newState) { - // the state action will redirect to console - var formData = { - 'view': 'state', - 'action': action, - 'apply': 1, - 'runState': runState, - 'newState': newState - }; - - $j("#pleasewait").toggleClass("hidden"); - - $j.ajax({ - type: 'POST', - url: thisUrl, - data: formData, - dataType: 'html', - enocde: true - }).done(function(data) { - location.reload(); - }); - } -}); diff --git a/web/skins/classic/views/js/state.js.php b/web/skins/classic/views/js/state.js.php deleted file mode 100644 index a7c14a0dc..000000000 --- a/web/skins/classic/views/js/state.js.php +++ /dev/null @@ -1,2 +0,0 @@ -var running = ; -var applying = ; diff --git a/web/skins/classic/views/js/stats.js b/web/skins/classic/views/js/stats.js new file mode 100644 index 000000000..ede551e31 --- /dev/null +++ b/web/skins/classic/views/js/stats.js @@ -0,0 +1,21 @@ +function initPage() { + var backBtn = $j('#backBtn'); + + // Disable the back button if there is nothing to go back to + backBtn.prop('disabled', !document.referrer.length); + + // Manage the BACK button + document.getElementById("backBtn").addEventListener("click", function onBackClick(evt) { + evt.preventDefault(); + window.history.back(); + }); + // Manage the REFRESH Button + document.getElementById("refreshBtn").addEventListener("click", function onRefreshClick(evt) { + evt.preventDefault(); + window.location.reload(true); + }); +} + +$j(document).ready(function() { + initPage(); +}); diff --git a/web/skins/classic/views/js/timeline.js b/web/skins/classic/views/js/timeline.js index 23f7b806e..85c73baa6 100644 --- a/web/skins/classic/views/js/timeline.js +++ b/web/skins/classic/views/js/timeline.js @@ -1,6 +1,8 @@ var events = {}; -function showEvent( eid, fid, width, height ) { +function showEvent(e) { + eid = e.getAttribute('data-event-id'); + fid = e.getAttribute('data-frame-id'); var url = '?view=event&eid='+eid+'&fid='+fid; url += filterQuery; window.location.href = url; @@ -13,73 +15,109 @@ function showEvent( eid, fid, width, height ) { vid.pause();*/ } -function createEventHtml( event, frame ) { - var eventHtml = new Element( 'div' ); +function createEventHtml(zm_event, frame) { + var eventHtml = new Element('div'); - if ( event.Archived > 0 ) { - eventHtml.addClass( 'archived' ); + if ( zm_event.Archived > 0 ) { + eventHtml.addClass('archived'); } - new Element( 'p' ).inject( eventHtml ).set( 'text', monitors[event.MonitorId].Name ); - new Element( 'p' ).inject( eventHtml ).set( 'text', event.Name+(frame?("("+frame.FrameId+")"):"") ); - new Element( 'p' ).inject( eventHtml ).set( 'text', event.StartTime+" - "+event.Length+"s" ); - new Element( 'p' ).inject( eventHtml ).set( 'text', event.Cause ); - if ( event.Notes ) { - new Element( 'p' ).inject( eventHtml ).set( 'text', event.Notes ); + new Element('p').inject(eventHtml).set('text', monitors[zm_event.MonitorId].Name); + new Element('p').inject(eventHtml).set('text', zm_event.Name+(frame?('('+frame.FrameId+')'):'')); + new Element('p').inject(eventHtml).set('text', zm_event.StartTime+' - '+zm_event.Length+'s'); + new Element('p').inject(eventHtml).set('text', zm_event.Cause); + if ( zm_event.Notes ) { + new Element('p').inject(eventHtml).set('text', zm_event.Notes); } - if ( event.Archived > 0 ) { - new Element( 'p' ).inject( eventHtml ).set( 'text', archivedString ); + if ( zm_event.Archived > 0 ) { + new Element('p').inject(eventHtml).set( 'text', archivedString); } - return ( eventHtml ); + return eventHtml; } -function showEventDetail( eventHtml ) { - $('instruction').addClass( 'hidden' ); +function showEventDetail(eventHtml) { + $('instruction').addClass('hidden'); $('eventData').empty(); - $('eventData').adopt( eventHtml ); - $('eventData').removeClass( 'hidden' ); + $('eventData').adopt(eventHtml); + $('eventData').removeClass('hidden'); } -function eventDataResponse( respObj, respText ) { - var event = respObj.event; - if ( !event ) { - console.log( "Null event" ); +function eventDataResponse(respObj, respText) { + var zm_event = respObj.event; + if ( !zm_event ) { + console.log('Null event'); return; } - events[event.Id] = event; + events[zm_event.Id] = zm_event; if ( respObj.loopback ) { - requestFrameData( event.Id, respObj.loopback ); + requestFrameData(zm_event.Id, respObj.loopback); } } function frameDataResponse( respObj, respText ) { var frame = respObj.frameimage; if ( !frame.FrameId ) { - console.log( "Null frame" ); + console.log('Null frame'); return; } - var event = events[frame.EventId]; - if ( !event ) { - console.error( "No event "+frame.eventId+" found" ); + var zm_event = events[frame.EventId]; + if ( !zm_event ) { + console.error('No event '+frame.eventId+' found'); return; } - if ( !event['frames'] ) { + if ( !zm_event['frames'] ) { console.log("No frames data in event response"); - event['frames'] = {}; + console.log(zm_event); + console.log(respObj); + zm_event['frames'] = {}; } - event['frames'][frame.FrameId] = frame; - event['frames'][frame.FrameId]['html'] = createEventHtml( event, frame ); + zm_event['frames'][frame.FrameId] = frame; + zm_event['frames'][frame.FrameId]['html'] = createEventHtml( zm_event, frame ); - previewEvent(frame.EventId, frame.FrameId); + showEventData(frame.EventId, frame.FrameId); } -var eventQuery = new Request.JSON( {url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: eventDataResponse} ); -var frameQuery = new Request.JSON( {url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: frameDataResponse} ); +function showEventData(eventId, frameId) { + if ( events[eventId] ) { + var zm_event = events[eventId]; + if ( zm_event['frames'] ) { + if ( zm_event['frames'][frameId] ) { + showEventDetail( zm_event['frames'][frameId]['html'] ); + var imagePath = 'index.php?view=image&eid='+eventId+'&fid='+frameId; + var videoName = zm_event.DefaultVideo; + loadEventImage( imagePath, eventId, frameId, zm_event.Width, zm_event.Height, zm_event.Frames/zm_event.Length, videoName, zm_event.Length, zm_event.StartTime, monitors[zm_event.MonitorId]); + return; + } else { + console.log('No frames for ' + frameId); + } + } else { + console.log('No frames'); + } + } else { + console.log('No event for ' + eventId); + } +} + +var eventQuery = new Request.JSON({ + url: thisUrl, + method: 'get', + timeout: AJAX_TIMEOUT, + link: 'cancel', + onSuccess: eventDataResponse +}); + +var frameQuery = new Request.JSON({ + url: thisUrl, + method: 'get', + timeout: AJAX_TIMEOUT, + link: 'cancel', + onSuccess: frameDataResponse +}); function requestFrameData( eventId, frameId ) { if ( !events[eventId] ) { @@ -91,24 +129,18 @@ function requestFrameData( eventId, frameId ) { } } -function previewEvent(eventId, frameId) { +function previewEvent(slot) { + eventId = slot.getAttribute('data-event-id'); + frameId = slot.getAttribute('data-frame-id'); if ( events[eventId] ) { - var event = events[eventId]; - if ( event['frames'] ) { - if ( event['frames'][frameId] ) { - showEventDetail( event['frames'][frameId]['html'] ); - var imagePath = 'index.php?view=image&eid='+eventId+'&fid='+frameId; - var videoName = event.DefaultVideo; - loadEventImage( imagePath, eventId, frameId, event.Width, event.Height, event.Frames/event.Length, videoName, event.Length, event.StartTime, monitors[event.MonitorId]); - return; - } - } + showEventData(eventId, frameId); + } else { + requestFrameData(eventId, frameId); } - requestFrameData( eventId, frameId ); } function loadEventImage( imagePath, eid, fid, width, height, fps, videoName, duration, startTime, Monitor ) { - var vid= $('preview'); + var vid = $('preview'); var imageSrc = $('imageSrc'); if ( videoName && vid ) { vid.show(); @@ -137,24 +169,75 @@ function loadEventImage( imagePath, eid, fid, width, height, fps, videoName, dur if ( vid ) vid.hide(); imageSrc.show(); imageSrc.setProperty('src', imagePath); - imageSrc.removeEvent('click'); - imageSrc.addEvent('click', showEvent.pass([eid, fid, width, height])); + imageSrc.setAttribute('data-event-id', eid); + imageSrc.setAttribute('data-frame-id', fid); + imageSrc.onclick=window['showEvent'].bind(imageSrc, imageSrc); } var eventData = $('eventData'); eventData.removeEvent('click'); - eventData.addEvent('click', showEvent.pass([eid, fid, width, height])); + eventData.addEvent('click', showEvent.pass()); } -function tlZoomBounds( minTime, maxTime ) { - console.log( "Zooming" ); +function tlZoomBounds(event) { + var target = event.target; + var minTime = target.getAttribute('data-zoom-min-time'); + var maxTime = target.getAttribute('data-zoom-max-time'); location.replace('?view='+currentView+filterQuery+'&minTime='+minTime+'&maxTime='+maxTime); } -function tlZoomRange( midTime, range ) { - location.replace('?view='+currentView+filterQuery+'&midTime='+midTime+'&range='+range); +function tlZoomOut() { + location.replace('?view='+currentView+filterQuery+'&midTime='+midTime+'&range='+zoomout_range); } -function tlPan( midTime, range ) { - location.replace('?view='+currentView+filterQuery+'&midTime='+midTime+'&range='+range); +function tlPanLeft() { + location.replace('?view='+currentView+filterQuery+'&midTime='+minTime+'&range='+range); } +function tlPanRight() { + location.replace('?view='+currentView+filterQuery+'&midTime='+maxTime+'&range='+range); +} + +window.addEventListener('DOMContentLoaded', function() { + // These look like the code in skin.js, but that code doesn't select for divs. + document.querySelectorAll('div.event').forEach(function(el) { + el.onclick = window[el.getAttribute('data-on-click-this')].bind(el, el); + el.onmouseover = window[el.getAttribute('data-on-mouseover-this')].bind(el, el); + }); + document.querySelectorAll('div.activity').forEach(function(el) { + el.onclick = window[el.getAttribute('data-on-click-this')].bind(el, el); + el.onmouseover = window[el.getAttribute('data-on-mouseover-this')].bind(el, el); + }); + document.querySelectorAll('div.zoom').forEach(function(el) { + el.onclick = function(ev) { + window[el.getAttribute('data-on-click')](ev); + }; + }); +}); + +function initPage() { + var backBtn = $j('#backBtn'); + + // Don't enable the back button if there is no previous zm page to go back to + backBtn.prop('disabled', !document.referrer.length); + + // Manage the BACK button + document.getElementById("backBtn").addEventListener("click", function onBackClick(evt) { + evt.preventDefault(); + window.history.back(); + }); + + // Manage the REFRESH Button + document.getElementById("refreshBtn").addEventListener("click", function onRefreshClick(evt) { + evt.preventDefault(); + window.location.reload(true); + }); + // Manage the LIST Button + document.getElementById("listBtn").addEventListener("click", function onListClick(evt) { + evt.preventDefault(); + window.location.assign('?view=events'+filterQuery); + }); +} + +$j(document).ready(function() { + initPage(); +}); diff --git a/web/skins/classic/views/js/timeline.js.php b/web/skins/classic/views/js/timeline.js.php index 79f17918d..474e403e0 100644 --- a/web/skins/classic/views/js/timeline.js.php +++ b/web/skins/classic/views/js/timeline.js.php @@ -1,3 +1,12 @@ + var filterQuery = ''; $field(); } + $jsMonitors[$monitor->Id()] = $jsMonitor; } ?> var monitors = ; var archivedString = ""; + +var minTime = ''; +var midTime = ''; +var maxTime = ''; +var range = ''; +var zoomout_range = ''; diff --git a/web/skins/classic/views/js/user.js b/web/skins/classic/views/js/user.js index 65c05c546..d491514e3 100644 --- a/web/skins/classic/views/js/user.js +++ b/web/skins/classic/views/js/user.js @@ -12,16 +12,22 @@ function validateForm( form, newUser ) { } else if ( newUser ) { errors[errors.length] = "You must supply a password"; } - var monitorIds = new Array(); - for ( var i = 0; i < form.elements['monitorIds'].options.length; i++ ) { - if ( form.elements['monitorIds'].options[i].selected ) { - monitorIds[monitorIds.length] = form.elements['monitorIds'].options[i].value; - } - } - form.elements['newUser[MonitorIds]'].value = monitorIds.join( ',' ); if ( errors.length ) { - alert( errors.join( "\n" ) ); - return ( false ); + alert(errors.join("\n")); + return false; } - return ( true ); + return true; } + +function initPage() { + $j('#contentForm').submit(function(event) { + if ( validateForm(this) ) { + $j('#contentButtons').hide(); + return true; + } else { + return false; + }; + }); +} // end function initPage + +window.addEventListener('DOMContentLoaded', initPage); diff --git a/web/skins/classic/views/js/video.js b/web/skins/classic/views/js/video.js index cfe2b2384..5f2df6ee3 100644 --- a/web/skins/classic/views/js/video.js +++ b/web/skins/classic/views/js/video.js @@ -1,9 +1,11 @@ -function deleteVideo( index ) { - window.location.replace( thisUrl+'?view='+currentView+'&eid='+eventId+'&deleteIndex='+index ); +function deleteVideo(e) { + index = e.getAttribute('data-file-index'); + window.location.replace(thisUrl+'?view='+currentView+'&eid='+eventId+'&deleteIndex='+index); } -function downloadVideo( index ) { - window.location.replace( thisUrl+'?view='+currentView+'&eid='+eventId+'&downloadIndex='+index ); +function downloadVideo(e) { + index = e.getAttribute('data-file-index'); + window.location.replace(thisUrl+'?view='+currentView+'&eid='+eventId+'&downloadIndex='+index); } var generateVideoTimer = null; @@ -11,24 +13,25 @@ var generateVideoTimer = null; function generateVideoProgress() { var tickerText = $('videoProgressTicker').get('text'); if ( tickerText.length < 1 || tickerText.length > 4 ) { - $('videoProgressTicker').set( 'text', '.' ); + $('videoProgressTicker').set('text', '.'); } else { - $('videoProgressTicker').appendText( '.' ); + $('videoProgressTicker').appendText('.'); } } function generateVideoResponse( respObj, respText ) { - window.location.replace( thisUrl+'?view='+currentView+'&eid='+eventId+'&generated='+((respObj.result=='Ok')?1:0) ); + window.location.replace(thisUrl+'?view='+currentView+'&eid='+eventId+'&generated='+((respObj.result=='Ok')?1:0)); } -function generateVideo( form ) { +function generateVideo() { + form = $j('#contentForm')[0]; var parms = 'view=request&request=event&action=video'; parms += '&'+$(form).toQueryString(); - var query = new Request.JSON( {url: thisUrl, method: 'post', data: parms, onSuccess: generateVideoResponse} ); + var query = new Request.JSON({url: thisUrl, method: 'post', data: parms, onSuccess: generateVideoResponse}); query.send(); - $('videoProgress').removeClass( 'hidden' ); - $('videoProgress').setProperty( 'class', 'warnText' ); - $('videoProgressText').set( 'text', videoGenProgressString ); + $('videoProgress').removeClass('hidden'); + $('videoProgress').setProperty('class', 'warnText'); + $('videoProgressText').set('text', videoGenProgressString); generateVideoProgress(); - generateVideoTimer = generateVideoProgress.periodical( 500 ); + generateVideoTimer = generateVideoProgress.periodical(500); } diff --git a/web/skins/classic/views/js/video.js.php b/web/skins/classic/views/js/video.js.php index cd12fef7b..e9bb78878 100644 --- a/web/skins/classic/views/js/video.js.php +++ b/web/skins/classic/views/js/video.js.php @@ -1,4 +1,7 @@ -var eventId = ''; + +var eventId = 'Id() ?>'; var videoGenSuccessString = ''; var videoGenFailedString = ''; diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index eceb994cc..7fb92fed5 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -1,39 +1,33 @@ -function setButtonState(element, butClass) { - if ( element ) { - element.className = butClass; - element.disabled = (butClass != 'inactive'); - } -} function showEvents() { - $('ptzControls').addClass( 'hidden' ); - $('events').removeClass( 'hidden' ); + $('ptzControls').addClass('hidden'); + $('events').removeClass('hidden'); if ( $('eventsControl') ) { $('eventsControl').addClass('hidden'); } if ( $('controlControl') ) { $('controlControl').removeClass('hidden'); } - showMode = "events"; + showMode = 'events'; } function showPtzControls() { - $('events').addClass( 'hidden' ); - $('ptzControls').removeClass( 'hidden' ); + $('events').addClass('hidden'); + $('ptzControls').removeClass('hidden'); if ( $('eventsControl') ) { $('eventsControl').removeClass('hidden'); } if ( $('controlControl') ) { $('controlControl').addClass('hidden'); } - showMode = "control"; + showMode = 'control'; } function changeScale() { var scale = $('scale').get('value'); var newWidth; var newHeight; - if (scale == "auto") { + if ( scale == '0' || scale == 'auto' ) { var newSize = scaleToFit(monitorWidth, monitorHeight, $j('#liveStream'+monitorId), $j('#replayStatus')); newWidth = newSize.width; newHeight = newSize.height; @@ -44,17 +38,17 @@ function changeScale() { newHeight = monitorHeight * scale / SCALE_BASE; } - Cookie.write( 'zmWatchScale'+monitorId, scale, {duration: 10*365} ); + Cookie.write('zmWatchScale'+monitorId, scale, {duration: 10*365}); /*Stream could be an applet so can't use moo tools*/ var streamImg = $('liveStream'+monitorId); if ( streamImg ) { - streamImg.style.width = newWidth + "px"; - streamImg.style.height = newHeight + "px"; + streamImg.style.width = newWidth + 'px'; + streamImg.style.height = newHeight + 'px'; streamImg.src = streamImg.src.replace(/scale=\d+/i, 'scale='+(scale== 'auto' ? autoScale : scale)); } else { - console.error("No element found for liveStream."); + console.error('No element found for liveStream'+monitorId); } } @@ -64,17 +58,17 @@ var lastAlarmState = STATE_IDLE; function setAlarmState( currentAlarmState ) { alarmState = currentAlarmState; - var stateClass = ""; + var stateClass = ''; if ( alarmState == STATE_ALARM ) { - stateClass = "alarm"; + stateClass = 'alarm'; } else if ( alarmState == STATE_ALERT ) { - stateClass = "alert"; + stateClass = 'alert'; } - $('stateValue').set( 'text', stateStrings[alarmState] ); + $('stateValue').set('text', stateStrings[alarmState]); if ( stateClass ) { - $('stateValue').setProperty( 'class', stateClass ); + $('stateValue').setProperty('class', stateClass); } else { - $('stateValue').removeProperty( 'class' ); + $('stateValue').removeProperty('class'); } var isAlarmed = ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ); @@ -87,7 +81,7 @@ function setAlarmState( currentAlarmState ) { if ( SOUND_ON_ALARM ) { // Enable the alarm sound if ( !canPlayPauseAudio ) { - $('alarmSound').removeClass( 'hidden' ); + $('alarmSound').removeClass('hidden'); } else { $('MediaPlayer').Play(); } @@ -96,25 +90,23 @@ function setAlarmState( currentAlarmState ) { window.focus(); } } - if ( SOUND_ON_ALARM ) { - if ( oldAlarm ) { + if ( oldAlarm ) { // done with an event do a refresh + if ( SOUND_ON_ALARM ) { // Disable alarm sound if ( !canPlayPauseAudio ) { - $('alarmSound').addClass( 'hidden' ); + $('alarmSound').addClass('hidden'); } else { $('MediaPlayer').Stop(); } } - } - if (oldAlarm) { // done with an event do a refresh eventCmdQuery(); } lastAlarmState = alarmState; -} +} // end function setAlarmState( currentAlarmState ) if ( monitorType != 'WebSite' ) { - var streamCmdParms = "view=request&request=stream&connkey="+connKey; + var streamCmdParms = 'view=request&request=stream&connkey='+connKey; if ( auth_hash ) { streamCmdParms += '&auth='+auth_hash; } @@ -141,7 +133,7 @@ function getStreamCmdFailure(xhr) { console.log(xhr); } function getStreamCmdResponse(respObj, respText) { - watchdogOk("stream"); + watchdogOk('stream'); if ( streamCmdTimer ) { streamCmdTimer = clearTimeout(streamCmdTimer); } @@ -155,11 +147,11 @@ function getStreamCmdResponse(respObj, respText) { $('levelValue').set('text', streamStatus.level); if ( streamStatus.level > 95 ) { - $('levelValue').className = "alarm"; + $('levelValue').className = 'alarm'; } else if ( streamStatus.level > 80 ) { - $('levelValue').className = "alert"; + $('levelValue').className = 'alert'; } else { - $('levelValue').className = "ok"; + $('levelValue').className = 'ok'; } var delayString = secsToTime(streamStatus.delay); @@ -194,51 +186,55 @@ function getStreamCmdResponse(respObj, respText) { } } // rate } else { - $('modeValue').set( 'text', "Live" ); + $('modeValue').set( 'text', 'Live' ); $('rate').addClass( 'hidden' ); $('delay').addClass( 'hidden' ); $('level').addClass( 'hidden' ); - streamCmdPlay( false ); + streamCmdPlay(false); } // end if paused or delayed - $('zoomValue').set( 'text', streamStatus.zoom ); - if ( streamStatus.zoom == "1.0" ) { - setButtonState( $('zoomOutBtn'), 'unavail' ); + $('zoomValue').set('text', streamStatus.zoom); + if ( streamStatus.zoom == '1.0' ) { + setButtonState('zoomOutBtn', 'unavail'); } else { - setButtonState( $('zoomOutBtn'), 'inactive' ); + setButtonState('zoomOutBtn', 'inactive'); } if ( canEditMonitors ) { if ( streamStatus.enabled ) { - $('enableAlarmsLink').addClass( 'hidden' ); - $('disableAlarmsLink').removeClass( 'hidden' ); + $('enableAlarmsLink').addClass('hidden'); + $('disableAlarmsLink').removeClass('hidden'); if ( streamStatus.forced ) { - $('forceAlarmLink').addClass( 'hidden' ); - $('cancelAlarmLink').removeClass( 'hidden' ); + $('forceAlarmLink').addClass('hidden'); + $('cancelAlarmLink').removeClass('hidden'); } else { - $('forceAlarmLink').removeClass( 'hidden' ); - $('cancelAlarmLink').addClass( 'hidden' ); + $('forceAlarmLink').removeClass('hidden'); + $('cancelAlarmLink').addClass('hidden'); } - $('forceCancelAlarm').removeClass( 'hidden' ); + $('forceCancelAlarm').removeClass('hidden'); } else { - $('enableAlarmsLink').removeClass( 'hidden' ); - $('disableAlarmsLink').addClass( 'hidden' ); - $('forceCancelAlarm').addClass( 'hidden' ); + $('enableAlarmsLink').removeClass('hidden'); + $('disableAlarmsLink').addClass('hidden'); + $('forceCancelAlarm').addClass('hidden'); } - $('enableDisableAlarms').removeClass( 'hidden' ); + $('enableDisableAlarms').removeClass('hidden'); } // end if canEditMonitors if ( streamStatus.auth ) { - console.log("Have a new auth hash" + streamStatus.auth); + auth_hash = streamStatus.auth; // Try to reload the image stream. var streamImg = $('liveStream'); if ( streamImg ) { streamImg.src = streamImg.src.replace(/auth=\w+/i, 'auth='+streamStatus.auth); } + streamCmdParms = streamCmdParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth); + statusCmdParms = statusCmdParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth); + eventCmdParms = eventCmdParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth); + controlParms = controlParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth); } // end if have a new auth hash } // end if respObj.status } else { - checkStreamForErrors("getStreamCmdResponse", respObj);//log them + checkStreamForErrors('getStreamCmdResponse', respObj);//log them // Try to reload the image stream. // If it's an auth error, we should reload the whole page. window.location.reload(); @@ -246,9 +242,9 @@ function getStreamCmdResponse(respObj, respText) { var streamImg = $('liveStream'+monitorId); if ( streamImg ) { streamImg.src = streamImg.src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) )); - console.log("Changing livestream src to " + streamImg.src); + console.log('Changing livestream src to ' + streamImg.src); } else { - console.log("Unable to find streamImg liveStream"); + console.log('Unable to find streamImg liveStream'); } } } @@ -257,132 +253,152 @@ function getStreamCmdResponse(respObj, respText) { if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ) { streamCmdTimeout = streamCmdTimeout/5; } - streamCmdTimer = streamCmdQuery.delay( streamCmdTimeout ); + streamCmdTimer = streamCmdQuery.delay(streamCmdTimeout); } function streamCmdPause( action ) { - setButtonState( $('pauseBtn'), 'active' ); - setButtonState( $('playBtn'), 'inactive' ); - setButtonState( $('stopBtn'), 'inactive' ); - setButtonState( $('fastFwdBtn'), 'inactive' ); - setButtonState( $('slowFwdBtn'), 'inactive' ); - setButtonState( $('slowRevBtn'), 'inactive' ); - setButtonState( $('fastRevBtn'), 'inactive' ); + setButtonState('pauseBtn', 'active'); + setButtonState('playBtn', 'inactive'); + setButtonState('stopBtn', 'inactive'); + if ( monitorStreamReplayBuffer ) { + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'inactive'); + setButtonState('slowRevBtn', 'inactive'); + setButtonState('fastRevBtn', 'inactive'); + } if ( action ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_PAUSE ); + streamCmdReq.send(streamCmdParms+"&command="+CMD_PAUSE); } } function streamCmdPlay( action ) { - setButtonState( $('pauseBtn'), 'inactive' ); - setButtonState( $('playBtn'), 'active' ); + setButtonState('pauseBtn', 'inactive'); + setButtonState('playBtn', 'active'); if ( streamStatus.delayed == true ) { - setButtonState( $('stopBtn'), 'inactive' ); - setButtonState( $('fastFwdBtn'), 'inactive' ); - setButtonState( $('slowFwdBtn'), 'inactive' ); - setButtonState( $('slowRevBtn'), 'inactive' ); - setButtonState( $('fastRevBtn'), 'inactive' ); + setButtonState('stopBtn', 'inactive'); + if ( monitorStreamReplayBuffer ) { + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'inactive'); + setButtonState('slowRevBtn', 'inactive'); + setButtonState('fastRevBtn', 'inactive'); + } } else { - setButtonState( $('stopBtn'), 'unavail' ); - setButtonState( $('fastFwdBtn'), 'unavail' ); - setButtonState( $('slowFwdBtn'), 'unavail' ); - setButtonState( $('slowRevBtn'), 'unavail' ); - setButtonState( $('fastRevBtn'), 'unavail' ); + setButtonState('stopBtn', 'unavail'); + if ( monitorStreamReplayBuffer ) { + setButtonState('fastFwdBtn', 'unavail'); + setButtonState('slowFwdBtn', 'unavail'); + setButtonState('slowRevBtn', 'unavail'); + setButtonState('fastRevBtn', 'unavail'); + } } if ( action ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_PLAY ); + streamCmdReq.send(streamCmdParms+"&command="+CMD_PLAY); } } function streamCmdStop( action ) { - setButtonState( $('pauseBtn'), 'inactive' ); - setButtonState( $('playBtn'), 'unavail' ); - setButtonState( $('stopBtn'), 'active' ); - setButtonState( $('fastFwdBtn'), 'unavail' ); - setButtonState( $('slowFwdBtn'), 'unavail' ); - setButtonState( $('slowRevBtn'), 'unavail' ); - setButtonState( $('fastRevBtn'), 'unavail' ); - if ( action ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_STOP ); + setButtonState('pauseBtn', 'inactive'); + setButtonState('playBtn', 'unavail'); + setButtonState('stopBtn', 'active'); + if ( monitorStreamReplayBuffer ) { + setButtonState('fastFwdBtn', 'unavail'); + setButtonState('slowFwdBtn', 'unavail'); + setButtonState('slowRevBtn', 'unavail'); + setButtonState('fastRevBtn', 'unavail'); } - setButtonState( $('stopBtn'), 'unavail' ); - setButtonState( $('playBtn'), 'active' ); + if ( action ) { + streamCmdReq.send(streamCmdParms+"&command="+CMD_STOP); + } + setButtonState('stopBtn', 'unavail'); + setButtonState('playBtn', 'active'); } function streamCmdFastFwd( action ) { - setButtonState( $('pauseBtn'), 'inactive' ); - setButtonState( $('playBtn'), 'inactive' ); - setButtonState( $('stopBtn'), 'inactive' ); - setButtonState( $('fastFwdBtn'), 'inactive' ); - setButtonState( $('slowFwdBtn'), 'inactive' ); - setButtonState( $('slowRevBtn'), 'inactive' ); - setButtonState( $('fastRevBtn'), 'inactive' ); + setButtonState('pauseBtn', 'inactive'); + setButtonState('playBtn', 'inactive'); + setButtonState('stopBtn', 'inactive'); + if ( monitorStreamReplayBuffer ) { + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'inactive'); + setButtonState('slowRevBtn', 'inactive'); + setButtonState('fastRevBtn', 'inactive'); + } if ( action ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_FASTFWD ); + streamCmdReq.send(streamCmdParms+"&command="+CMD_FASTFWD); } } function streamCmdSlowFwd( action ) { - setButtonState( $('pauseBtn'), 'inactive' ); - setButtonState( $('playBtn'), 'inactive' ); - setButtonState( $('stopBtn'), 'inactive' ); - setButtonState( $('fastFwdBtn'), 'inactive' ); - setButtonState( $('slowFwdBtn'), 'active' ); - setButtonState( $('slowRevBtn'), 'inactive' ); - setButtonState( $('fastRevBtn'), 'inactive' ); - if ( action ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_SLOWFWD ); + setButtonState('pauseBtn', 'inactive'); + setButtonState('playBtn', 'inactive'); + setButtonState('stopBtn', 'inactive'); + if ( monitorStreamReplayBuffer ) { + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'active'); + setButtonState('slowRevBtn', 'inactive'); + setButtonState('fastRevBtn', 'inactive'); + } + if ( action ) { + streamCmdReq.send(streamCmdParms+"&command="+CMD_SLOWFWD); + } + setButtonState('pauseBtn', 'active'); + if ( monitorStreamReplayBuffer ) { + setButtonState('slowFwdBtn', 'inactive'); } - setButtonState( $('pauseBtn'), 'active' ); - setButtonState( $('slowFwdBtn'), 'inactive' ); } function streamCmdSlowRev( action ) { - setButtonState( $('pauseBtn'), 'inactive' ); - setButtonState( $('playBtn'), 'inactive' ); - setButtonState( $('stopBtn'), 'inactive' ); - setButtonState( $('fastFwdBtn'), 'inactive' ); - setButtonState( $('slowFwdBtn'), 'inactive' ); - setButtonState( $('slowRevBtn'), 'active' ); - setButtonState( $('fastRevBtn'), 'inactive' ); - if ( action ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_SLOWREV ); + setButtonState('pauseBtn', 'inactive'); + setButtonState('playBtn', 'inactive'); + setButtonState('stopBtn', 'inactive'); + if ( monitorStreamReplayBuffer ) { + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'inactive'); + setButtonState('slowRevBtn', 'active'); + setButtonState('fastRevBtn', 'inactive'); + } + if ( action ) { + streamCmdReq.send(streamCmdParms+"&command="+CMD_SLOWREV); + } + setButtonState('pauseBtn', 'active'); + if ( monitorStreamReplayBuffer ) { + setButtonState('slowRevBtn', 'inactive'); } - setButtonState( $('pauseBtn'), 'active' ); - setButtonState( $('slowRevBtn'), 'inactive' ); } function streamCmdFastRev( action ) { - setButtonState( $('pauseBtn'), 'inactive' ); - setButtonState( $('playBtn'), 'inactive' ); - setButtonState( $('stopBtn'), 'inactive' ); - setButtonState( $('fastFwdBtn'), 'inactive' ); - setButtonState( $('slowFwdBtn'), 'inactive' ); - setButtonState( $('slowRevBtn'), 'inactive' ); - setButtonState( $('fastRevBtn'), 'inactive' ); + setButtonState('pauseBtn', 'inactive'); + setButtonState('playBtn', 'inactive'); + setButtonState('stopBtn', 'inactive'); + if ( monitorStreamReplayBuffer ) { + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'inactive'); + setButtonState('slowRevBtn', 'inactive'); + setButtonState('fastRevBtn', 'inactive'); + } if ( action ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_FASTREV ); + streamCmdReq.send(streamCmdParms+"&command="+CMD_FASTREV); } } function streamCmdZoomIn( x, y ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_ZOOMIN+"&x="+x+"&y="+y ); + streamCmdReq.send(streamCmdParms+"&command="+CMD_ZOOMIN+"&x="+x+"&y="+y); } function streamCmdZoomOut() { - streamCmdReq.send( streamCmdParms+"&command="+CMD_ZOOMOUT ); + streamCmdReq.send(streamCmdParms+"&command="+CMD_ZOOMOUT); } function streamCmdScale( scale ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_SCALE+"&scale="+scale ); + streamCmdReq.send(streamCmdParms+"&command="+CMD_SCALE+"&scale="+scale); } function streamCmdPan( x, y ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_PAN+"&x="+x+"&y="+y ); + streamCmdReq.send(streamCmdParms+"&command="+CMD_PAN+"&x="+x+"&y="+y); } function streamCmdQuery() { - streamCmdReq.send( streamCmdParms+"&command="+CMD_QUERY ); + streamCmdReq.send(streamCmdParms+"&command="+CMD_QUERY); } if ( monitorType != 'WebSite' ) { @@ -390,12 +406,18 @@ if ( monitorType != 'WebSite' ) { if ( auth_hash ) { statusCmdParms += '&auth='+auth_hash; } - var statusCmdReq = new Request.JSON( {url: monitorUrl, method: 'get', data: statusCmdParms, timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getStatusCmdResponse} ); + var statusCmdReq = new Request.JSON( { + url: monitorUrl, + method: 'get', + timeout: AJAX_TIMEOUT, + link: 'cancel', + onSuccess: getStatusCmdResponse + } ); var statusCmdTimer = null; } function getStatusCmdResponse(respObj, respText) { - watchdogOk("status"); + watchdogOk('status'); if ( statusCmdTimer ) { statusCmdTimer = clearTimeout(statusCmdTimer); } @@ -404,22 +426,22 @@ function getStatusCmdResponse(respObj, respText) { $('fpsValue').set('text', respObj.monitor.FrameRate); setAlarmState(respObj.monitor.Status); } else { - checkStreamForErrors("getStatusCmdResponse", respObj); + checkStreamForErrors('getStatusCmdResponse', respObj); } var statusCmdTimeout = statusRefreshTimeout; if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ) { statusCmdTimeout = statusCmdTimeout/5; } - statusCmdTimer = statusCmdQuery.delay( statusCmdTimeout ); + statusCmdTimer = statusCmdQuery.delay(statusCmdTimeout); } function statusCmdQuery() { - statusCmdReq.send(); + statusCmdReq.send(statusCmdParms); } if ( monitorType != 'WebSite' ) { - var alarmCmdParms = "view=request&request=alarm&id="+monitorId; + var alarmCmdParms = 'view=request&request=alarm&id='+monitorId; if ( auth_hash ) { alarmCmdParms += '&auth='+auth_hash; } @@ -434,8 +456,8 @@ if ( monitorType != 'WebSite' ) { var alarmCmdFirst = true; } -function getAlarmCmdResponse( respObj, respText ) { - checkStreamForErrors("getAlarmCmdResponse", respObj); +function getAlarmCmdResponse(respObj, respText) { + checkStreamForErrors('getAlarmCmdResponse', respObj); } function cmdDisableAlarms() { @@ -448,14 +470,14 @@ function cmdEnableAlarms() { function cmdForceAlarm() { alarmCmdReq.send(alarmCmdParms+"&command=forceAlarm"); - if (window.event) { + if ( window.event ) { window.event.preventDefault(); } } function cmdCancelForcedAlarm() { alarmCmdReq.send(alarmCmdParms+"&command=cancelForcedAlarm"); - if (window.event) { + if ( window.event ) { window.event.preventDefault(); } return false; @@ -471,16 +493,18 @@ function getActResponse( respObj, respText ) { eventCmdQuery(); } -function deleteEvent( event, eventId ) { - var actParms = "view=request&request=event&action=delete&id="+eventId; +function deleteEvent(event, eventId) { + var actParms = 'view=request&request=event&action=delete&id='+eventId; + if ( auth_hash ) { + actParms += '&auth='+auth_hash; + } var actReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: 3000, - data: actParms, onSuccess: getActResponse } ); - actReq.send(); + actReq.send(actParms); event.stop(); } @@ -493,7 +517,6 @@ if ( monitorType != 'WebSite' ) { url: monitorUrl, method: 'get', timeout: AJAX_TIMEOUT, - data: eventCmdParms, link: 'cancel', onSuccess: getEventCmdResponse, onTimeout: eventCmdQuery @@ -507,58 +530,78 @@ function highlightRow( row ) { } function getEventCmdResponse( respObj, respText ) { - watchdogOk("event"); + watchdogOk('event'); if ( eventCmdTimer ) { - eventCmdTimer = clearTimeout( eventCmdTimer ); + eventCmdTimer = clearTimeout(eventCmdTimer); } if ( respObj.result == 'Ok' ) { var dbEvents = respObj.events.reverse(); var eventList = $('eventList'); - var eventListBody = $(eventList).getElement( 'tbody' ); - var eventListRows = $(eventListBody).getElements( 'tr' ); + var eventListBody = $(eventList).getElement('tbody'); + var eventListRows = $(eventListBody).getElements('tr'); - eventListRows.each( function( row ) { - row.removeClass( 'updated' ); + eventListRows.each( function(row) { + row.removeClass('updated'); } ); for ( var i = 0; i < dbEvents.length; i++ ) { - var event = dbEvents[i]; - var row = $('event'+event.Id); + var zm_event = dbEvents[i]; + var row = $('event'+zm_event.Id); var newEvent = (row == null ? true : false); if ( newEvent ) { - row = new Element( 'tr', {'id': 'event'+event.Id} ); - new Element( 'td', {'class': 'colId'} ).inject( row ); - new Element( 'td', {'class': 'colName'} ).inject( row ); - new Element( 'td', {'class': 'colTime'} ).inject( row ); - new Element( 'td', {'class': 'colSecs'} ).inject( row ); - new Element( 'td', {'class': 'colFrames'} ).inject( row ); - new Element( 'td', {'class': 'colScore'} ).inject( row ); - new Element( 'td', {'class': 'colDelete'} ).inject( row ); + row = new Element('tr', {'id': 'event'+zm_event.Id}); + new Element('td', {'class': 'colId'}).inject(row); + new Element('td', {'class': 'colName'}).inject(row); + new Element('td', {'class': 'colTime'}).inject(row); + new Element('td', {'class': 'colSecs'}).inject(row); + new Element('td', {'class': 'colFrames'}).inject(row); + new Element('td', {'class': 'colScore'}).inject(row); + new Element('td', {'class': 'colDelete'}).inject(row); - var link = new Element( 'a', {'href': '#', 'events': {'click': createEventPopup.pass( [event.Id, '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId+'&page=1&popup=1', event.Width, event.Height] )}}); - link.set( 'text', event.Id ); - link.inject( row.getElement( 'td.colId' ) ); + var link = new Element('a', { + 'href': '#', + 'events': { + 'click': createEventPopup.pass( [ + zm_event.Id, + '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId+'&page=1&popup=1', + zm_event.Width, + zm_event.Height + ] ) + } + }); + link.set('text', zm_event.Id); + link.inject(row.getElement('td.colId')); - link = new Element( 'a', {'href': '#', 'events': {'click': createEventPopup.pass( [event.Id, '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId+'&page=1&popup=1', event.Width, event.Height] )}}); - link.set( 'text', event.Name ); - link.inject( row.getElement( 'td.colName' ) ); + link = new Element('a', { + 'href': '#', + 'events': { + 'click': createEventPopup.pass( [ + zm_event.Id, + '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId+'&page=1&popup=1', + zm_event.Width, + zm_event.Height + ] ) + } + }); + link.set('text', zm_event.Name); + link.inject(row.getElement('td.colName')); - row.getElement( 'td.colTime' ).set( 'text', event.StartTime ); - row.getElement( 'td.colSecs' ).set( 'text', event.Length ); + row.getElement('td.colTime').set('text', zm_event.StartTime); + row.getElement('td.colSecs').set('text', zm_event.Length); - link = new Element( 'a', {'href': '#', 'events': {'click': createFramesPopup.pass( [event.Id, event.Width, event.Height] )}}); - link.set( 'text', event.Frames+'/'+event.AlarmFrames ); - link.inject( row.getElement( 'td.colFrames' ) ); + link = new Element('a', {'href': '#', 'events': {'click': createFramesPopup.pass( [zm_event.Id, zm_event.Width, zm_event.Height] )}}); + link.set('text', zm_event.Frames+'/'+zm_event.AlarmFrames); + link.inject(row.getElement('td.colFrames')); - link = new Element( 'a', {'href': '#', 'events': {'click': createFramePopup.pass( [event.Id, '0', event.Width, event.Height] )}}); - link.set( 'text', event.AvgScore+'/'+event.MaxScore ); - link.inject( row.getElement( 'td.colScore' ) ); + link = new Element('a', {'href': '#', 'events': {'click': createFramePopup.pass( [zm_event.Id, '0', zm_event.Width, zm_event.Height] )}}); + link.set('text', zm_event.AvgScore+'/'+zm_event.MaxScore); + link.inject(row.getElement('td.colScore')); - link = new Element( 'button', { + link = new Element('button', { 'type': 'button', 'title': deleteString, - 'data-event-id': event.Id, + 'data-event-id': zm_event.Id, 'events': { 'click': function(e) { var event_id = e.target.getAttribute('data-event-id'); @@ -573,30 +616,30 @@ function getEventCmdResponse( respObj, respText ) { 'mouseout': highlightRow.pass(row) } }); - link.set( 'text', 'X' ); - link.inject( row.getElement( 'td.colDelete' ) ); + link.set('text', 'X'); + link.inject(row.getElement('td.colDelete')); if ( i == 0 ) { - row.inject( $(eventListBody) ); + row.inject($(eventListBody)); } else { - row.inject( $(eventListBody), 'top' ); + row.inject($(eventListBody), 'top'); if ( !eventCmdFirst ) { - row.addClass( 'recent' ); + row.addClass('recent'); } } } else { - row.getElement( 'td.colName a' ).set( 'text', event.Name ); - row.getElement( 'td.colSecs' ).set( 'text', event.Length ); - row.getElement( 'td.colFrames a' ).set( 'text', event.Frames+'/'+event.AlarmFrames ); - row.getElement( 'td.colScore a' ).set( 'text', event.AvgScore+'/'+event.MaxScore ); - row.removeClass( 'recent' ); + row.getElement('td.colName a').set('text', zm_event.Name); + row.getElement('td.colSecs').set('text', zm_event.Length); + row.getElement('td.colFrames a').set('text', zm_event.Frames+'/'+zm_event.AlarmFrames); + row.getElement('td.colScore a').set('text', zm_event.AvgScore+'/'+zm_event.MaxScore); + row.removeClass('recent'); } - row.addClass( 'updated' ); - } + row.addClass('updated'); + } // end foreach event - var rows = $(eventListBody).getElements( 'tr' ); + var rows = $(eventListBody).getElements('tr'); for ( var i = 0; i < rows.length; i++ ) { - if ( !rows[i].hasClass( 'updated' ) ) { + if ( !rows[i].hasClass('updated') ) { rows[i].destroy(); rows.splice( i, 1 ); i--; @@ -607,46 +650,56 @@ function getEventCmdResponse( respObj, respText ) { rows.length--; } } else { - checkStreamForErrors("getEventCmdResponse", respObj); - } + checkStreamForErrors('getEventCmdResponse', respObj); + } // end if objresult == ok var eventCmdTimeout = eventsRefreshTimeout; if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ) { eventCmdTimeout = eventCmdTimeout/5; } - eventCmdTimer = eventCmdQuery.delay( eventCmdTimeout ); + eventCmdTimer = eventCmdQuery.delay(eventCmdTimeout); eventCmdFirst = false; } function eventCmdQuery() { if ( eventCmdTimer ) { // avoid firing another if we are firing one - eventCmdTimer = clearTimeout( eventCmdTimer ); + eventCmdTimer = clearTimeout(eventCmdTimer); } - eventCmdReq.send(); + eventCmdReq.send(eventCmdParms); } if ( monitorType != 'WebSite' ) { - var controlParms = "view=request&request=control&id="+monitorId; + var controlParms = 'view=request&request=control&id='+monitorId; if ( auth_hash ) { controlParms += '&auth='+auth_hash; } - var controlReq = new Request.JSON( {url: monitorUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getControlResponse} ); + var controlReq = new Request.JSON( { + url: monitorUrl, + method: 'post', + timeout: AJAX_TIMEOUT, + link: 'cancel', + onSuccess: getControlResponse + } ); } -function getControlResponse( respObj, respText ) { +function getControlResponse(respObj, respText) { if ( !respObj ) { return; } //console.log( respText ); if ( respObj.result != 'Ok' ) { - alert( "Control response was status = "+respObj.status+"\nmessage = "+respObj.message ); + alert("Control response was status = "+respObj.status+"\nmessage = "+respObj.message); } } -function controlCmd( control, event, xtell, ytell ) { - var locParms = ""; +function controlCmd(event) { + button = event.target; + control = button.getAttribute('value'); + xtell = button.getAttribute('data-xtell'); + ytell = button.getAttribute('data-ytell'); + + var locParms = ''; if ( event && (xtell || ytell) ) { - console.log(event); var target = event.target; var coords = $(target).getCoordinates(); @@ -654,37 +707,37 @@ function controlCmd( control, event, xtell, ytell ) { var y = event.pageY - coords.top; if ( xtell ) { - var xge = parseInt( (x*100)/coords.width ); + var xge = parseInt((x*100)/coords.width); if ( xtell == -1 ) { xge = 100 - xge; } else if ( xtell == 2 ) { xge = 2*(50 - xge); } - locParms += "&xge="+xge; + locParms += '&xge='+xge; } if ( ytell ) { - var yge = parseInt( (y*100)/coords.height ); + var yge = parseInt((y*100)/coords.height); if ( ytell == -1 ) { yge = 100 - yge; } else if ( ytell == 2 ) { yge = 2*(50 - yge); } - locParms += "&yge="+yge; + locParms += '&yge='+yge; } } - controlReq.send( controlParms+"&control="+control+locParms ); - if ( streamMode == "single" ) { - fetchImage.pass( $('imageFeed').getElement('img') ).delay( 1000 ); + controlReq.send(controlParms+"&control="+control+locParms); + if ( streamMode == 'single' ) { + fetchImage.pass($('imageFeed').getElement('img')).delay(1000); } } function controlCmdImage( x, y ) { var imageControlParms = controlParms; - imageControlParms += "&scale="+scale; - imageControlParms += "&control="+imageControlMode; + imageControlParms += '&scale='+scale; + imageControlParms += '&control='+imageControlMode; controlReq.send( imageControlParms+"&x="+x+"&y="+y ); - if ( streamMode == "single" ) { + if ( streamMode == 'single' ) { fetchImage.pass( $('imageFeed').getElement('img') ).delay( 1000 ); } } @@ -694,30 +747,35 @@ function fetchImage( streamImage ) { } function handleClick( event ) { - var target = event.target; - var x = event.page.x - $(target).getLeft(); - var y = event.page.y - $(target).getTop(); + var $target = $(event.target); + var scaleX = parseInt(monitorWidth / $target.getWidth()); + var scaleY = parseInt(monitorHeight / $target.getHeight()); + var x = (event.page.x - $target.getLeft()) * scaleX; + var y = (event.page.y - $target.getTop()) * scaleY; - if ( showMode == "events" || !imageControlMode ) { + if ( showMode == 'events' || !imageControlMode ) { if ( event.shift ) { streamCmdPan( x, y ); } else if ( event.event.ctrlKey ) { - console.log("Zooming out"); streamCmdZoomOut(); } else { - streamCmdZoomIn( x, y ); + streamCmdZoomIn(x, y); } } else { - controlCmdImage( x, y ); + controlCmdImage(x, y); } } function appletRefresh() { if ( streamStatus && (!streamStatus.paused && !streamStatus.delayed) ) { - var streamImg = $('liveStream'); - var parent = streamImg.getParent(); - streamImg.dispose(); - streamImg.inject( parent ); + var streamImg = $('liveStream'+monitorId); + if ( streamImg ) { + var parent = streamImg.getParent(); + streamImg.dispose(); + streamImg.inject( parent ); + } else { + console.error("Nothing found for liveStream"+monitorId); + } if ( appletRefreshTime ) { appletRefresh.delay( appletRefreshTime*1000 ); } @@ -759,7 +817,7 @@ function reloadWebSite() { function initPage() { if ( monitorType != 'WebSite' ) { - if ( streamMode == "single" ) { + if ( streamMode == 'single' ) { statusCmdTimer = statusCmdQuery.delay( (Math.random()+0.1)*statusRefreshTimeout ); watchdogCheck.pass('status').periodical(statusRefreshTimeout*2); } else { @@ -770,28 +828,35 @@ function initPage() { eventCmdTimer = eventCmdQuery.delay( (Math.random()+0.1)*statusRefreshTimeout ); watchdogCheck.pass('event').periodical(eventsRefreshTimeout*2); - if ( canStreamNative || streamMode == "single" ) { + if ( canStreamNative || (streamMode == 'single') ) { var streamImg = $('imageFeed').getElement('img'); if ( !streamImg ) { streamImg = $('imageFeed').getElement('object'); } - if ( streamMode == "single" ) { - streamImg.addEvent('click', fetchImage.pass(streamImg)); - fetchImage.pass(streamImg).periodical(imageRefreshTimeout); + if ( !streamImg ) { + console.error('No streamImg found for imageFeed'); } else { - streamImg.addEvent('click', function(event) { - handleClick(event); - }); - } - } + if ( streamMode == 'single' ) { + streamImg.addEvent('click', fetchImage.pass(streamImg)); + fetchImage.pass(streamImg).periodical(imageRefreshTimeout); + } else { + streamImg.addEvent('click', function(event) { + handleClick(event); + }); + } + } // end if have streamImg + } // streamMode native or single if ( refreshApplet && appletRefreshTime ) { appletRefresh.delay(appletRefreshTime*1000); } - if ( scale == "auto" ) changeScale(); + if ( scale == '0' || scale == 'auto' ) changeScale(); if ( window.history.length == 1 ) { $j('#closeControl').html(''); } + document.querySelectorAll('select[name="scale"]').forEach(function(el) { + el.onchange = window['changeScale']; + }); } else if ( monitorRefresh > 0 ) { setInterval(reloadWebSite, monitorRefresh*1000); } diff --git a/web/skins/classic/views/js/watch.js.php b/web/skins/classic/views/js/watch.js.php index 15a43a6ce..365d62aed 100644 --- a/web/skins/classic/views/js/watch.js.php +++ b/web/skins/classic/views/js/watch.js.php @@ -1,3 +1,10 @@ + // // Import constants // @@ -44,13 +51,13 @@ var showMode = "'; var maxDisplayEvents = ; - var monitorId = Id() ?>; -var monitorWidth = Width() ?>; -var monitorHeight = Height() ?>; -var monitorUrl = 'UrlToIndex(); ?>'; -var monitorType = 'Type() ) ?>'; -var monitorRefresh = 'Refresh() ) ?>'; +var monitorWidth = ViewWidth() ?>; +var monitorHeight = ViewHeight() ?>; +var monitorUrl = 'UrlToIndex() ?>'; +var monitorType = 'Type() ?>'; +var monitorRefresh = 'Refresh() ?>'; +var monitorStreamReplayBuffer = StreamReplayBuffer() ?>; var scale = ''; @@ -63,11 +70,13 @@ var canStreamNative = ; var canPlayPauseAudio = Browser.ie; -CanMoveMap() ) { ?> +Control(); + if ( $control->CanMoveMap() ) { ?> var imageControlMode = "moveMap"; -CanMoveRel() ) { ?> +CanMoveRel() ) { ?> var imageControlMode = "movePseudoMap"; -CanMoveCon() ) { ?> +CanMoveCon() ) { ?> var imageControlMode = "moveConMap"; var imageControlMode = null; diff --git a/web/skins/classic/views/js/zone.js b/web/skins/classic/views/js/zone.js index 8ed5d4544..22c7689cb 100644 --- a/web/skins/classic/views/js/zone.js +++ b/web/skins/classic/views/js/zone.js @@ -1,3 +1,7 @@ +var requestQueue = new Request.Queue({ + concurrent: monitorData.length, + stopOnFailure: false +}); function validateForm( form ) { var errors = new Array(); if ( selfIntersecting ) { @@ -48,13 +52,13 @@ function validateForm( form ) { } } if ( errors.length ) { - alert( errors.join( "\n" ) ); + alert(errors.join("\n")); return false; } return true; } -function submitForm( form ) { +function submitForm(form) { form.elements['newZone[AlarmRGB]'].value = (form.newAlarmRgbR.value<<16)|(form.newAlarmRgbG.value<<8)|form.newAlarmRgbB.value; form.elements['newZone[NumCoords]'].value = zone['Points'].length; form.elements['newZone[Coords]'].value = getCoordString(); @@ -175,16 +179,25 @@ function applyPreset() { } } -function toPixels( field, maxValue ) { +function toPixels(field, maxValue) { if ( field.value != '' ) { field.value = Math.round((field.value*maxValue)/100); + if ( field.value > maxValue ) field.value = maxValue; } + field.setAttribute('step', 1); + field.setAttribute('max', maxValue); } -function toPercent( field, maxValue ) { +// maxValue is the max Pixels value which is normally the max area +function toPercent(field, maxValue) { if ( field.value != '' ) { field.value = Math.round((100*100*field.value)/maxValue)/100; + if ( field.value > 100 ) { + field.value = 100; + } } + field.setAttribute('step', 0.01); + field.setAttribute('max', 100); } function applyZoneUnits() { @@ -193,62 +206,77 @@ function applyZoneUnits() { var form = document.zoneForm; if ( form.elements['newZone[Units]'].value == 'Pixels' ) { form.elements['newZone[TempArea]'].value = area; - toPixels( form.elements['newZone[MinAlarmPixels]'], area ); - toPixels( form.elements['newZone[MaxAlarmPixels]'], area ); - toPixels( form.elements['newZone[MinFilterPixels]'], area ); - toPixels( form.elements['newZone[MaxFilterPixels]'], area ); - toPixels( form.elements['newZone[MinBlobPixels]'], area ); - toPixels( form.elements['newZone[MaxBlobPixels]'], area ); + toPixels(form.elements['newZone[MinAlarmPixels]'], area); + toPixels(form.elements['newZone[MaxAlarmPixels]'], area); + toPixels(form.elements['newZone[MinFilterPixels]'], area); + toPixels(form.elements['newZone[MaxFilterPixels]'], area); + toPixels(form.elements['newZone[MinBlobPixels]'], area); + toPixels(form.elements['newZone[MaxBlobPixels]'], area); } else { - form.elements['newZone[TempArea]'].value = Math.round( area/monitorArea * 100 ); - toPercent( form.elements['newZone[MinAlarmPixels]'], area ); - toPercent( form.elements['newZone[MaxAlarmPixels]'], area ); - toPercent( form.elements['newZone[MinFilterPixels]'], area ); - toPercent( form.elements['newZone[MaxFilterPixels]'], area ); - toPercent( form.elements['newZone[MinBlobPixels]'], area ); - toPercent( form.elements['newZone[MaxBlobPixels]'], area ); + form.elements['newZone[TempArea]'].value = Math.round(area/monitorArea * 100); + toPercent(form.elements['newZone[MinAlarmPixels]'], area); + toPercent(form.elements['newZone[MaxAlarmPixels]'], area); + toPercent(form.elements['newZone[MinFilterPixels]'], area); + toPercent(form.elements['newZone[MaxFilterPixels]'], area); + toPercent(form.elements['newZone[MinBlobPixels]'], area); + toPercent(form.elements['newZone[MaxBlobPixels]'], area); } } -function limitRange( field, minValue, maxValue ) { +function limitRange(field, minValue, maxValue) { if ( field.value != '' ) { - field.value = constrainValue( parseInt(field.value), parseInt(minValue), parseInt(maxValue) ); + field.value = constrainValue( + parseInt(field.value), + parseInt(minValue), + parseInt(maxValue) + ); } } -function limitFilter( field ) { +function limitRangeToUnsignedByte(field) { + if ( field.value != '' ) { + field.value = constrainValue(parseInt(field.value), 0, 255); + } +} + +function limitFilter(field) { field.value = (Math.floor((field.value-1)/2)*2) + 1; field.value = constrainValue(parseInt(field.value), 3, 15); } -function limitArea( field ) { +function limitArea(field) { var minValue = 0; var maxValue = zone.Area; - if ( document.zoneForm.elements['newZone[Units]'].value == "Percent" ) { + if ( document.zoneForm.elements['newZone[Units]'].value == 'Percent' ) { maxValue = 100; } - limitRange( field, minValue, maxValue ); + limitRange(field, minValue, maxValue); } -function highlightOn( index ) { - $('row'+index).addClass( 'highlight' ); - $('point'+index).addClass( 'highlight' ); +function highlightOn(index) { + $('row'+index).addClass('highlight'); + $('point'+index).addClass('highlight'); } -function highlightOff( index ) { - $('row'+index).removeClass( 'highlight' ); - $('point'+index).removeClass( 'highlight' ); +function highlightOff(index) { + row = $('row'+index); + if ( row ) { + row.removeClass('highlight'); + } else { + console.log("No row for index " + index); + } + $('point'+index).removeClass('highlight'); } -function setActivePoint( index ) { - highlightOff( index ); - $('row'+index).addClass( 'active' ); - $('point'+index).addClass( 'active' ); +function setActivePoint(index) { + highlightOff(index); + $('row'+index).addClass('active'); + $('point'+index).addClass('active'); } -function unsetActivePoint( index ) { - $('row'+index).removeClass( 'active' ); - $('point'+index).removeClass( 'active' ); +function unsetActivePoint(index) { + $('row'+index).removeClass('active'); + $('point'+index).removeClass('active'); } function getCoordString() { @@ -256,28 +284,33 @@ function getCoordString() { for ( var i = 0; i < zone['Points'].length; i++ ) { coords[coords.length] = zone['Points'][i].x+','+zone['Points'][i].y; } - return ( coords.join( " " ) ); + return coords.join(' '); } function updateZoneImage() { + var imageFrame = $('imageFrame'); + var style = imageFrame.currentStyle || window.getComputedStyle(imageFrame); + + scale = (imageFrame.clientWidth - ( style.paddingLeft.toInt() + style.paddingRight.toInt() )) / maxX; var SVG = $('zoneSVG'); var Poly = $('zonePoly'); Poly.points.clear(); for ( var i = 0; i < zone['Points'].length; i++ ) { var Point = SVG.createSVGPoint(); Point.x = zone['Points'][i].x; - Point.y = zone['Points'][i].y; - Poly.points.appendItem( Point ); + //+ 2*padding_left; + Point.y = zone['Points'][i].y;// + 2*padding_top; + Poly.points.appendItem(Point); } } -function fixActivePoint( index ) { - updateActivePoint( index ); - unsetActivePoint( index ); +function fixActivePoint(index) { + updateActivePoint(index); + unsetActivePoint(index); updateZoneImage(); } -function constrainValue( value, loVal, hiVal ) { +function constrainValue(value, loVal, hiVal) { if ( value < loVal ) { return loVal; } @@ -287,45 +320,59 @@ function constrainValue( value, loVal, hiVal ) { return value; } -function updateActivePoint( index ) { +function updateActivePoint(index) { var point = $('point'+index); - var x = constrainValue( point.getStyle( 'left' ).toInt(), 0, maxX ); - var y = constrainValue( point.getStyle( 'top' ).toInt(), 0, maxY ); + var imageFrame = $('imageFrame'); + var style = imageFrame.currentStyle || window.getComputedStyle(imageFrame); + var padding_left = style.paddingLeft.toInt(); + var padding_top = style.paddingTop.toInt(); - $('newZone[Points]['+index+'][x]').value = x; - $('newZone[Points]['+index+'][y]').value = y; - zone['Points'][index].x = x; - zone['Points'][index].y = y; + scale = (imageFrame.clientWidth - ( style.paddingLeft.toInt() + style.paddingRight.toInt() )) / maxX; + var left = point.getStyle('left').toInt(); + + if ( left < padding_left ) { + point.setStyle('left', style.paddingLeft); + left = padding_left.toInt(); + } + var top = point.getStyle('top').toInt(); + if ( top < padding_top ) { + point.setStyle('top', style.paddingTop); + top = padding_top; + } + + var x = constrainValue(Math.ceil(left / scale)-Math.ceil(padding_left/scale), 0, maxX); + var y = constrainValue(Math.ceil(top / scale)-Math.ceil(padding_top/scale), 0, maxY); + + zone['Points'][index].x = $('newZone[Points]['+index+'][x]').value = x; + zone['Points'][index].y = $('newZone[Points]['+index+'][y]').value = y; var Point = $('zonePoly').points.getItem(index); - Point.x =x; - Point.y =y; + Point.x = x; + Point.y = y; updateArea(); -} +} // end function updateActivePoint(index) -function addPoint( index ) { +function addPoint(index) { var nextIndex = index+1; if ( index >= (zone['Points'].length-1) ) { nextIndex = 0; } + var newX = parseInt(Math.round((zone['Points'][index]['x']+zone['Points'][nextIndex]['x'])/2)); var newY = parseInt(Math.round((zone['Points'][index]['y']+zone['Points'][nextIndex]['y'])/2)); if ( nextIndex == 0 ) { zone['Points'][zone['Points'].length] = {'x': newX, 'y': newY}; } else { - zone['Points'].splice( nextIndex, 0, {'x': newX, 'y': newY} ); + zone['Points'].splice(nextIndex, 0, {'x': newX, 'y': newY}); } drawZonePoints(); - // drawZonePoints calls updateZoneImage - //updateZoneImage(); - //setActivePoint( nextIndex ); } -function delPoint( index ) { - zone['Points'].splice( index, 1 ); +function delPoint(index) { + zone['Points'].splice(index, 1); drawZonePoints(); } -function limitPointValue( point, loVal, hiVal ) { +function limitPointValue(point, loVal, hiVal) { point.value = constrainValue(point.value, loVal, hiVal); } @@ -339,42 +386,45 @@ function updateArea( ) { } else if ( form.elements['newZone[Units]'].value == 'Pixels' ) { form.elements['newZone[TempArea]'].value = area; } else { - alert("Unknown units: " + form.elements['newZone[Units]'].value); + alert('Unknown units: ' + form.elements['newZone[Units]'].value); } } -function updateX( index ) { - limitPointValue( $('newZone[Points]['+index+'][x]'), 0, maxX ); +function updateX(input) { + index = input.getAttribute('data-point-index'); + + limitPointValue(input, 0, maxX); var point = $('point'+index); - var x = $('newZone[Points]['+index+'][x]').get('value'); + var x = input.value; - point.setStyle( 'left', x+'px' ); + point.setStyle('left', x+'px'); zone['Points'][index].x = x; var Point = $('zonePoly').points.getItem(index); Point.x = x; updateArea(); } -function updateY( index ) { - limitPointValue( $('newZone[Points]['+index+'][y]'), 0, maxY ); +function updateY(input) { + index = input.getAttribute('data-point-index'); + limitPointValue(input, 0, maxY); var point = $('point'+index); - var y = $('newZone[Points]['+index+'][y]').get('value'); + var y = input.value; - point.setStyle( 'top', y+'px' ); + point.setStyle('top', y+'px'); zone['Points'][index].y = y; var Point = $('zonePoly').points.getItem(index); Point.y = y; updateArea(); } -function saveChanges( element ) { +function saveChanges(element) { var form = element.form; if ( validateForm(form) ) { submitForm(form); if ( form.elements['newZone[Type]'].value == 'Privacy' ) { - alert( 'Capture process for this monitor will be restarted for the Privacy zone changes to take effect.' ); + alert('Capture process for this monitor will be restarted for the Privacy zone changes to take effect.'); } return true; } @@ -382,286 +432,124 @@ function saveChanges( element ) { } function drawZonePoints() { - $('imageFrame').getElements( 'div.zonePoint' ).each( function( element ) { - element.destroy(); - } ); + var imageFrame = $('imageFrame'); + imageFrame.getElements('.zonePoint').each( + function(element) { + element.destroy(); + }); + var style = imageFrame.currentStyle || window.getComputedStyle(imageFrame); + scale = (imageFrame.clientWidth - ( style.paddingLeft.toInt() + style.paddingRight.toInt() )) / maxX; + console.log("Scale = width: " + imageFrame.clientWidth); + for ( var i = 0; i < zone['Points'].length; i++ ) { - var div = new Element( 'div', { + console.log("scale: " + scale + " x " + zone['Points'][i].x + " = " + Math.round(zone['Points'][i].x * scale)); + var div = new Element('div', { 'id': 'point'+i, + 'data-point-index': i, 'class': 'zonePoint', 'title': 'Point '+(i+1), 'styles': { - 'left': zone['Points'][i].x, - 'top': zone['Points'][i].y + 'left': (Math.round(zone['Points'][i].x * scale) + style.paddingLeft.toInt())+"px", + 'top': ((zone['Points'][i].y * scale).toInt() + style.paddingTop.toInt()) +"px" } - } ); - div.addEvent( 'mouseover', highlightOn.pass( i ) ); - div.addEvent( 'mouseout', highlightOff.pass( i ) ); - div.inject( $('imageFrame') ); + }); + div.addEvent('mouseover', highlightOn.pass(i)); + div.addEvent('mouseout', highlightOff.pass(i)); + div.inject(imageFrame); div.makeDraggable( { - 'container': $('imageFrame'), - 'onStart': setActivePoint.pass( i ), - 'onComplete': fixActivePoint.pass( i ), - 'onDrag': updateActivePoint.pass( i ) + 'container': imageFrame, + 'onStart': setActivePoint.pass(i), + 'onComplete': fixActivePoint.pass(i), + 'onDrag': updateActivePoint.pass(i) } ); - } + } // end foreach point - var tables = $('zonePoints').getElements( 'table' ); - tables.each( function( table ) { - table.getElement( 'tbody' ).empty(); + var tables = $('zonePoints').getElement('table').getElements('table'); + tables.each( function(table) { + table.getElement('tbody').empty(); } ); + for ( var i = 0; i < zone['Points'].length; i++ ) { - var row = new Element( 'tr', {'id': 'row'+i} ); - row.addEvents( {'mouseover': highlightOn.pass( i ), 'mouseout': highlightOff.pass( i )} ); - var cell = new Element( 'td' ); - cell.set( 'text', i+1 ); - cell.inject( row ); + var row; + row = new Element('tr', {'id': 'row'+i}); + row.addEvent('mouseover', highlightOn.pass(i)); + row.addEvent('mouseout', highlightOff.pass(i)); + //row.onmouseover = highlightOn.pass(i) + //row.onmouseout = window['highlightOff'].bind(div, div); + var cell = new Element('td'); + cell.set('text', i+1); + cell.inject(row); - cell = new Element( 'td' ); - var input = new Element( 'input', {'id': 'newZone[Points]['+i+'][x]', 'name': 'newZone[Points]['+i+'][x]', 'value': zone['Points'][i].x, 'size': 5} ); - input.addEvent( 'input', updateX.pass( i ) ); - input.inject( cell ); - cell.inject( row ); + cell = new Element('td'); + var input = new Element('input', { + 'id': 'newZone[Points]['+i+'][x]', + 'name': 'newZone[Points]['+i+'][x]', + 'value': zone['Points'][i].x, + 'type': 'number', + 'class': 'ZonePoint', + 'min': '0', + 'max': maxX, + 'data-point-index': i + }); + input.oninput = window['updateX'].bind(input, input); + input.inject(cell); + cell.inject(row); - cell = new Element( 'td' ); - input = new Element( 'input', {'id': 'newZone[Points]['+i+'][y]', 'name': 'newZone[Points]['+i+'][y]', 'value': zone['Points'][i].y, 'size': 5} ); - input.addEvent( 'input', updateY.pass( i ) ); - input.inject( cell ); - cell.inject( row ); + cell = new Element('td'); + input = new Element('input', { + 'id': 'newZone[Points]['+i+'][y]', + 'name': 'newZone[Points]['+i+'][y]', + 'value': zone['Points'][i].y, + 'type': 'number', + 'class': 'ZonePoint', + 'min': '0', + 'max': maxY, + 'data-point-index': i + } ); + input.oninput = window['updateY'].bind(input, input); + input.inject(cell); + cell.inject(row); - cell = new Element( 'td' ); - new Element( 'button', { + cell = new Element('td'); + new Element('button', { 'type': 'button', 'events': {'click': addPoint.pass(i)} - }).set( 'text', '+' ).inject( cell ); + }).set('text', '+').inject(cell); if ( zone['Points'].length > 3 ) { cell.appendText(' '); - new Element( 'button', { + new Element('button', { 'id': 'delete'+i, 'type': 'button', 'events': {'click': delPoint.pass(i)} - } ).set( 'text', '-' ).inject( cell ); + }).set('text', '-').inject(cell); } - cell.inject( row ); + cell.inject(row); - row.inject( tables[i%tables.length].getElement( 'tbody' ) ); - } + row.inject(tables[i%tables.length].getElement('tbody')); + } // end foreach point // Sets up the SVG polygon updateZoneImage(); } -// -// Imported from watch.js and modified for new zone edit view -// - -var alarmState = STATE_IDLE; -var lastAlarmState = STATE_IDLE; - -function setAlarmState( currentAlarmState ) { - alarmState = currentAlarmState; - - var stateClass = ""; - if ( alarmState == STATE_ALARM ) { - stateClass = "alarm"; - } else if ( alarmState == STATE_ALERT ) { - stateClass = "alert"; +function streamCmdPause() { + for ( var i = 0, length = monitors.length; i < length; i++ ) { + monitors[i].pause(); } - $('stateValue').set( 'text', stateStrings[alarmState] ); - if ( stateClass ) { - $('stateValue').setProperty( 'class', stateClass ); - } else { - $('stateValue').removeProperty( 'class' ); - } - - var isAlarmed = ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ); - var wasAlarmed = ( lastAlarmState == STATE_ALARM || lastAlarmState == STATE_ALERT ); - - var newAlarm = ( isAlarmed && !wasAlarmed ); - var oldAlarm = ( !isAlarmed && wasAlarmed ); - - if ( newAlarm ) { - if ( SOUND_ON_ALARM ) { - // Enable the alarm sound - if ( !canPlayPauseAudio ) { - $('alarmSound').removeClass( 'hidden' ); - } else { - $('MediaPlayer').Play(); - } - } - } - if ( SOUND_ON_ALARM ) { - if ( oldAlarm ) { - // Disable alarm sound - if ( !canPlayPauseAudio ) { - $('alarmSound').addClass( 'hidden' ); - } else { - $('MediaPlayer').Stop(); - } - } - } - lastAlarmState = alarmState; + document.getElementById('pauseBtn').style.display = 'none'; + document.getElementById('playBtn').style.display = 'inline'; } -var streamCmdParms = "view=request&request=stream&connkey="+connKey; -if ( auth_hash ) { - streamCmdParms += '&auth='+auth_hash; -} -var streamCmdReq = new Request.JSON( { - url: monitorUrl, - method: 'get', - timeout: AJAX_TIMEOUT, - link: 'cancel', - onSuccess: getStreamCmdResponse -} ); -var streamCmdTimer = null; - -var streamStatus; - -function getStreamCmdResponse( respObj, respText ) { - watchdogOk("stream"); - if ( streamCmdTimer ) { - streamCmdTimer = clearTimeout( streamCmdTimer ); - } - - if ( respObj.result == 'Ok' ) { - streamStatus = respObj.status; - $('fpsValue').set( 'text', streamStatus.fps ); - - setAlarmState( streamStatus.state ); - - if ( streamStatus.paused == true ) { - streamCmdPause( false ); - } else if ( streamStatus.delayed == true && streamStatus.rate == 1 ) { - streamCmdPlay( false ); - } - } else { - checkStreamForErrors("getStreamCmdResponse", respObj);//log them - if ( ! streamPause ) { - // Try to reload the image stream. - var streamImg = $('liveStream'+monitorId); - if ( streamImg ) { - streamImg.src = streamImg.src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) )); - } - } - } - - if ( ! streamPause ) { - var streamCmdTimeout = statusRefreshTimeout; - if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ) { - streamCmdTimeout = streamCmdTimeout/5; - } - streamCmdTimer = streamCmdQuery.delay( streamCmdTimeout ); +function streamCmdPlay() { + for ( var i = 0, length = monitors.length; i < length; i++ ) { + monitors[i].play(); } + document.getElementById('playBtn').style.display = 'none'; + document.getElementById('pauseBtn').style.display = 'inline'; } -var streamPause = false; - -function streamCmdPauseToggle() { - if ( streamPause == true ) { - streamCmdPlay( true ); - streamPause = false; - document.getElementById("pauseBtn").value = pauseString; - } else { - streamCmdPause( true ); - streamPause = true; - document.getElementById("pauseBtn").value = playString; - } -} - -function streamCmdPause( action ) { - if ( action ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_PAUSE ); - } -} - -function streamCmdPlay( action ) { - if ( action ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_PLAY ); - } -} - -function streamCmdStop( action ) { - if ( action ) { - streamCmdReq.send( streamCmdParms+"&command="+CMD_STOP ); - } -} - -function streamCmdQuery() { - streamCmdReq.send( streamCmdParms+"&command="+CMD_QUERY ); -} - -var statusCmdParms = "view=request&request=status&entity=monitor&id="+monitorId+"&element[]=Status&element[]=FrameRate"; -if ( auth_hash ) { - statusCmdParms += '&auth='+auth_hash; -} -var statusCmdReq = new Request.JSON( { - url: monitorUrl, - method: 'get', - data: statusCmdParms, - timeout: AJAX_TIMEOUT, - link: 'cancel', - onSuccess: getStatusCmdResponse -} ); -var statusCmdTimer = null; - -function getStatusCmdResponse( respObj, respText ) { - watchdogOk("status"); - if ( statusCmdTimer ) { - statusCmdTimer = clearTimeout( statusCmdTimer ); - } - - if ( respObj.result == 'Ok' ) { - $('fpsValue').set( 'text', respObj.monitor.FrameRate ); - setAlarmState( respObj.monitor.Status ); - } else { - checkStreamForErrors("getStatusCmdResponse", respObj); - } - - if ( ! streamPause ) { - var statusCmdTimeout = statusRefreshTimeout; - if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ) { - statusCmdTimeout = statusCmdTimeout/5; - } - statusCmdTimer = statusCmdQuery.delay( statusCmdTimeout ); - } -} - -function statusCmdQuery() { - statusCmdReq.send(); -} - -function fetchImage( streamImage ) { - streamImage.src = streamImage.src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) )); -} - -function appletRefresh() { - if ( streamStatus && (!streamStatus.paused && !streamStatus.delayed) ) { - var streamImg = $('liveStream'); - var parent = streamImg.getParent(); - streamImg.dispose(); - streamImg.inject( parent ); - if ( appletRefreshTime ) { - appletRefresh.delay( appletRefreshTime*1000 ); - } - } else { - appletRefresh.delay( 15*1000 ); //if we are paused or delayed check every 15 seconds if we are live yet... - } -} - -var watchdogInactive = { - 'stream': false, - 'status': false -}; - -var watchdogFunctions = { - 'stream': streamCmdQuery, - 'status': statusCmdQuery -}; - //Make sure the various refreshes are still taking effect -function watchdogCheck( type ) { +function watchdogCheck(type) { if ( watchdogInactive[type] ) { - console.log( "Detected streamWatch of type: " + type + " stopped, restarting" ); watchdogFunctions[type](); watchdogInactive[type] = false; } else { @@ -669,9 +557,14 @@ function watchdogCheck( type ) { } } -function watchdogOk( type ) { +function watchdogOk(type) { watchdogInactive[type] = false; } +function presetSelectorBlur() { + this.selectedIndex = 0; +} + +var monitors = new Array(); function initPage() { var form = document.zoneForm; @@ -679,19 +572,59 @@ function initPage() { //form.elements['newZone[Name]'].disabled = true; //form.elements['newZone[Type]'].disabled = true; form.presetSelector.disabled = true; + form.presetSelector.onblur = window['presetSelectorBlur'].bind(form.presetSelector, form.presetSelector); //form.elements['newZone[Units]'].disabled = true; - form.newAlarmRgbR.disabled = true; - form.newAlarmRgbG.disabled = true; - form.newAlarmRgbB.disabled = true; - form.elements['newZone[CheckMethod]'].disabled = true; - form.elements['newZone[MinPixelThreshold]'].disabled = true; - form.elements['newZone[MaxPixelThreshold]'].disabled = true; - form.elements['newZone[MinAlarmPixels]'].disabled = true; - form.elements['newZone[MaxAlarmPixels]'].disabled = true; - form.elements['newZone[FilterX]'].disabled = true; - form.elements['newZone[FilterY]'].disabled = true; - form.elements['newZone[MinFilterPixels]'].disabled = true; - form.elements['newZone[MaxFilterPixels]'].disabled = true; + if ( CheckMethod = form.elements['newZone[CheckMethod]'] ) { + CheckMethod.disabled = true; + CheckMethod.onchange = window['applyCheckMethod'].bind(CheckMethod, CheckMethod); + } + + [ + 'newZone[MinPixelThreshold]', + 'newZone[MaxPixelThreshold]', + 'newAlarmRgbR', + 'newAlarmRgbG', + 'newAlarmRgbB', + ].forEach( + function(element_name, index) { + var el = form.elements[element_name]; + if ( el ) { + el.oninput = window['limitRangeToUnsignedByte'].bind(el, el); + el.disabled = true; + } else { + console.error("Element " + element_name + " not found in zone edit form"); + } + }); + [ + 'newZone[FilterX]', + 'newZone[FilterY]' + ].forEach( + function(element_name, index) { + var el = form.elements[element_name]; + if ( el ) { + el.oninput = window['limitFilter'].bind(el, el); + el.disabled = true; + } else { + console.error("Element " + element_name + " not found in zone edit form"); + } + } + ); + [ + 'newZone[MinAlarmPixels]', + 'newZone[MaxAlarmPixels]', + 'newZone[MinFilterPixels]', + 'newZone[MaxFilterPixels]' + ].forEach(function(element_name, index) { + var el = form.elements[element_name]; + if ( el ) { + el.oninput = window['limitArea'].bind(el, el); + el.disabled = true; + } else { + console.error("Element " + element_name + " not found in zone edit form"); + } + } + ); + form.elements['newZone[MinBlobPixels]'].disabled = true; form.elements['newZone[MaxBlobPixels]'].disabled = true; form.elements['newZone[MinBlobs]'].disabled = true; @@ -705,39 +638,64 @@ function initPage() { } applyCheckMethod(); + + $('pauseBtn').onclick = function() { + streamCmdPause(); + }; + $('playBtn').style.display = 'none'; // hide pause initially + $('playBtn').onclick = function() { + streamCmdPlay(); + }; + + if ( el = $('saveBtn') ) { + el.onclick = window['saveChanges'].bind(el, el); + } + if ( el = $('cancelBtn') ) { + el.onclick = function() { + refreshParentWindow(); + closeWindow(); + }; + } + + for ( var i = 0, length = monitorData.length; i < length; i++ ) { + monitors[i] = new MonitorStream(monitorData[i]); + + // Start the fps and status updates. give a random delay so that we don't assault the server + var delay = Math.round( (Math.random()+0.5)*statusRefreshTimeout ); + monitors[i].start(delay); + } + + document.querySelectorAll('#imageFrame img').forEach(function(el) { + el.addEventListener("load", imageLoadEvent, {passive: true}); + }); + window.addEventListener("resize", drawZonePoints, {passive: true}); + + // Manage the BACK button + document.getElementById("backBtn").addEventListener("click", function onBackClick(evt) { + evt.preventDefault(); + window.history.back(); + }); + + // Disable the back button if there is nothing to go back to + $j('#backBtn').prop('disabled', !document.referrer.length); + + // Manage the REFRESH Button + document.getElementById("refreshBtn").addEventListener("click", function onRefreshClick(evt) { + evt.preventDefault(); + window.location.reload(true); + }); +} // initPage + +function imageLoadEvent() { + // We only need this event on the first image load to set dimensions. + // Turn it off after it has been called. + document.querySelectorAll('#imageFrame img').forEach(function(el) { + el.removeEventListener("load", imageLoadEvent, {passive: true}); + }); drawZonePoints(); - - // - // Imported from watch.js and modified for new zone edit view - // - - var delay = (Math.random()+0.1)*statusRefreshTimeout; - //console.log("Delay for status updates is: " + delay ); - if ( streamMode == "single" ) { - statusCmdTimer = statusCmdQuery.delay( delay ); - watchdogCheck.pass('status').periodical(statusRefreshTimeout*2); - } else { - streamCmdTimer = streamCmdQuery.delay( delay ); - watchdogCheck.pass('stream').periodical(statusRefreshTimeout*2); - } - - if ( canStreamNative || streamMode == "single" ) { - var streamImg = $('imageFrame').getElement('img'); - if ( !streamImg ) { - streamImg = $('imageFrame').getElement('object'); - } - if ( streamMode == "single" ) { - streamImg.addEvent( 'click', fetchImage.pass( streamImg ) ); - fetchImage.pass( streamImg ).periodical( imageRefreshTimeout ); - } - } - - if ( refreshApplet && appletRefreshTime ) { - appletRefresh.delay( appletRefreshTime*1000 ); - } } -function Polygon_calcArea( coords ) { +function Polygon_calcArea(coords) { var n_coords = coords.length; var float_area = 0.0; @@ -748,7 +706,7 @@ function Polygon_calcArea( coords ) { } float_area += (coords[n_coords-1].x*coords[0].y - coords[0].x*coords[n_coords-1].y) / 2; - return Math.round( Math.abs( float_area ) ); + return Math.round(Math.abs(float_area)); } -window.addEventListener( 'DOMContentLoaded', initPage ); +window.addEventListener('DOMContentLoaded', initPage); diff --git a/web/skins/classic/views/js/zone.js.php b/web/skins/classic/views/js/zone.js.php index 069a0de54..9fadea32c 100644 --- a/web/skins/classic/views/js/zone.js.php +++ b/web/skins/classic/views/js/zone.js.php @@ -1,3 +1,14 @@ + + var presets = new Object(); ] = { 'ExtendAlarmFrames': '' }; var zone = { @@ -44,10 +55,22 @@ zone['Points'][] = { 'x': -var maxX = Width()-1 ?>; -var maxY = Height()-1 ?>; -var monitorArea = Width() * $monitor->Height() ?>; -var selfIntersecting = ; +var maxX = ViewWidth()-1 ?>; +var maxY = ViewHeight()-1 ?>; +var monitorArea = ViewWidth() * $monitor->ViewHeight() ?>; + +var monitorData = new Array(); +monitorData[monitorData.length] = { + 'id': Id() ?>, + 'connKey': connKey() ?>, + 'width': ViewWidth() ?>, + 'height':ViewHeight() ?>, + 'url': 'UrlToIndex( ZM_MIN_STREAMING_PORT ? ($monitor->Id() + ZM_MIN_STREAMING_PORT) : '') ?>', + 'type': 'Type() ?>', + 'refresh': 'Refresh() ?>' +}; + +var selfIntersecting = ; var selfIntersectingString = ''; var alarmRGBUnsetString = ''; @@ -65,6 +88,7 @@ var minBlobLtMinFilterString = ''; var minBlobsLtMaxString = ''; +var deleteString = ""; // // Imported from watch.js.php and modified for new zone edit view // @@ -82,10 +106,6 @@ stateStrings[STATE_ALARM] = ""; stateStrings[STATE_ALERT] = ""; stateStrings[STATE_TAPE] = ""; -var pauseString = ""; -var playString = ""; - -var deleteString = ""; var CMD_PAUSE = ; var CMD_PLAY = ; diff --git a/web/skins/classic/views/js/zones.js b/web/skins/classic/views/js/zones.js index 3e5306930..a0eec294e 100644 --- a/web/skins/classic/views/js/zones.js +++ b/web/skins/classic/views/js/zones.js @@ -1,9 +1,46 @@ -var streamCmdParms = "view=request&request=stream&connkey="+connKey; -var streamCmdReq = new Request.JSON( {url: monitorUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel'} ); -function streamCmdQuit( action ) { +var requestQueue = new Request.Queue({ + concurrent: monitorData.length, + stopOnFailure: false +}); +function streamCmdQuit( ) { if ( action ) { streamCmdReq.send( streamCmdParms+"&command="+CMD_QUIT ); } } +// Manage the Add New Zone button +function AddNewZone(el) { + url = el.getAttribute('data-url'); + window.location.assign(url); +} + +var monitors = new Array(); + +function initPage() { + for ( var i = 0, length = monitorData.length; i < length; i++ ) { + monitors[i] = new MonitorStream(monitorData[i]); + + // Start the fps and status updates. give a random delay so that we don't assault the server + var delay = Math.round( (Math.random()+0.5)*statusRefreshTimeout ); + monitors[i].start(delay); + } + + // Manage the BACK button + document.getElementById("backBtn").addEventListener("click", function onBackClick(evt) { + evt.preventDefault(); + window.history.back(); + }); + + // Disable the back button if there is nothing to go back to + $j('#backBtn').prop('disabled', !document.referrer.length); + + // Manage the REFRESH Button + document.getElementById("refreshBtn").addEventListener("click", function onRefreshClick(evt) { + evt.preventDefault(); + window.location.reload(true); + }); +} + +window.addEventListener('DOMContentLoaded', initPage); + diff --git a/web/skins/classic/views/js/zones.js.php b/web/skins/classic/views/js/zones.js.php index 6bd9c402c..180a19d81 100644 --- a/web/skins/classic/views/js/zones.js.php +++ b/web/skins/classic/views/js/zones.js.php @@ -1,4 +1,40 @@ -var connKey = ''; -var monitorUrl = 'UrlToIndex() ) ?>'; +var monitorData = new Array(); + +monitorData[monitorData.length] = { + 'id': Id() ?>, + 'connKey': connKey() ?>, + 'width': ViewWidth() ?>, + 'height':ViewHeight() ?>, + 'url': 'UrlToIndex( ZM_MIN_STREAMING_PORT ? ($monitor->Id() + ZM_MIN_STREAMING_PORT) : '') ?>', + 'type': 'Type() ?>', + 'refresh': 'Refresh() ?>' +}; + + +var STATE_IDLE = ; +var STATE_PREALARM = ; +var STATE_ALARM = ; +var STATE_ALERT = ; +var STATE_TAPE = ; + +var stateStrings = new Array(); +stateStrings[STATE_IDLE] = ""; +stateStrings[STATE_PREALARM] = ""; +stateStrings[STATE_ALARM] = ""; +stateStrings[STATE_ALERT] = ""; +stateStrings[STATE_TAPE] = ""; + + +var CMD_PAUSE = ; +var CMD_PLAY = ; +var CMD_STOP = ; +var CMD_QUERY = ; var CMD_QUIT = ; + +var statusRefreshTimeout = ; diff --git a/web/skins/classic/views/log.php b/web/skins/classic/views/log.php index c2ce0262e..8d2e9f293 100644 --- a/web/skins/classic/views/log.php +++ b/web/skins/classic/views/log.php @@ -25,122 +25,75 @@ if ( !canView('System') ) { $focusWindow = true; -xhtmlHeaders(__FILE__, translate('SystemLog') ); +xhtmlHeaders(__FILE__, translate('SystemLog')); ?> +
- MonitorIds(); @@ -127,5 +127,4 @@ echo htmlSelect('newGroup[ParentId]', $options, $newGroup->ParentId(), array('on - - + diff --git a/web/skins/classic/views/groups.php b/web/skins/classic/views/groups.php index 5ae28a40a..045e56cc0 100644 --- a/web/skins/classic/views/groups.php +++ b/web/skins/classic/views/groups.php @@ -55,7 +55,9 @@ xhtmlHeaders(__FILE__, translate('Groups'));
 '; if ( canEdit('Groups') ) { - $html .= ''. validHtmlStr($Group->Id() . ' ' . $Group->Name()).''; + $html .= ''.validHtmlStr($Group->Id().' '.$Group->Name()).''; } else { $html .= validHtmlStr($Group->Name()); } - $html .= ''. monitorIdsToNames($Group->MonitorIds(), 30).'
'. validHtmlStr(monitorIdsToNames($Group->MonitorIds(), 30)).'
- - - - - - -
-
- : / - : / - : / - : / - : -
-
-
- - - - - -
-
+
+ : / + : / + : / + : / + : +
+
+ + + + + +
-
-
- - - - - - - - - - - - -
- - - - - - - - -
- - - - - - - - -
- -
-
- - - - - - - - - - - - - - - - -
-
-
-
-
- -
-
-
-
-
-
-
-
- - - - -
-
- - - - - -
-
- : -
- - -
-
-
-
- - +
+
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + + + + + + + + + + + + + + +
+
+
+ + diff --git a/web/skins/classic/views/login.php b/web/skins/classic/views/login.php index 42a4090fc..e08bf91bc 100644 --- a/web/skins/classic/views/login.php +++ b/web/skins/classic/views/login.php @@ -1,5 +1,5 @@ @@ -7,7 +7,6 @@ xhtmlHeaders(__FILE__, translate('Login') );
- "; - } ?> - - - + + '; +} ?> +
- - + diff --git a/web/skins/classic/views/logout.php b/web/skins/classic/views/logout.php deleted file mode 100644 index d68bd2b82..000000000 --- a/web/skins/classic/views/logout.php +++ /dev/null @@ -1,48 +0,0 @@ - - -
- -
-
- - -

-

- - - -

-
-
-
- - diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index fb10b1b5f..bab87da40 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -21,7 +21,7 @@ require_once('includes/Server.php'); require_once('includes/Storage.php'); -if ( !canView('Monitors') ) { +if ( !canEdit('Monitors', empty($_REQUEST['mid'])?0:$_REQUEST['mid']) ) { $view = 'error'; return; } @@ -35,13 +35,13 @@ if ( !$Server ) { } $monitor = null; -if ( ! empty($_REQUEST['mid']) ) { - $monitor = new ZM\Monitor( $_REQUEST['mid'] ); +if ( !empty($_REQUEST['mid']) ) { + $monitor = new ZM\Monitor($_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']); @@ -49,86 +49,12 @@ if ( ! $monitor ) { if ( ZM_OPT_X10 ) $x10Monitor = dbFetchOne('SELECT * FROM TriggersX10 WHERE MonitorId = ?', NULL, array($_REQUEST['dupId'])); $clonedName = $monitor->Name(); - $monitor->Name(translate('Monitor').'-'.$nextId); $monitor->Id(0); } else { $monitor = new ZM\Monitor(); - $monitor->set( array( - 'Id' => 0, - 'Name' => translate('Monitor').'-'.$nextId, - 'Function' => 'Mocord', - 'Enabled' => true, - 'LinkedMonitors' => '', - 'Type' => 'Ffmpeg', - 'Device' => '/dev/video0', - 'Channel' => '0', - 'Format' => 0x000000ff, - 'Protocol' => '', - 'Method' => '', - 'Host' => '', - 'Path' => '', - 'Options' => '', - 'Port' => '80', - 'User' => '', - 'Pass' => '', - 'Colours' => 4, - 'Palette' => 0, - 'Width' => '', - 'Height' => '', - 'Orientation' => '0', - 'Deinterlacing' => 0, - 'RTSPDescribe' => 0, - 'SaveJPEGs' => '0', - 'VideoWriter' => '1', - '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' => '0', - 'LabelFormat' => '%N - %d/%m/%y %H:%M:%S', - 'LabelX' => 0, - 'LabelY' => 0, - 'LabelSize' => 1, - 'ImageBufferCount' => 20, - 'WarmupCount' => 0, - 'PreEventCount' => 0, - 'PostEventCount' => 5, - 'StreamReplayBuffer' => 0, - 'AlarmFrameCount' => 1, - 'Controllable' => 0, - 'ControlId' => '', - 'ControlType' => 0, - 'ControlDevice' => '', - 'ControlAddress' => '', - 'AutoStopTimeout' => '', - 'TrackMotion' => 0, - 'TrackDelay' => '', - 'ReturnLocation' => -1, - 'ReturnDelay' => '', - 'SectionLength' => 600, - 'MinSectionLength' => 10, - 'FrameSkip' => 0, - 'MotionFrameSkip' => 0, - 'EventPrefix' => 'Event-', - 'AnalysisFPSLimit' => '', - 'AnalysisUpdateDelay' => 0, - 'MaxFPS' => null, - 'AlarmMaxFPS' => null, - 'FPSReportInterval' => 100, - 'RefBlendPerc' => 6, - 'AlarmRefBlendPerc' => 6, - 'DefaultRate' => '100', - 'DefaultScale' => '100', - 'DefaultCodec' => 'auto', - 'SignalCheckPoints' => '10', - 'SignalCheckColour' => '#0000c0', - 'WebColour' => 'red', - 'Exif' => '0', - 'Triggers' => '', - 'V4LMultiBuffer' => '', - 'V4LCapturesPerFrame' => 1, - 'ServerId' => 'auto', - 'StorageId' => '1', - 'Refresh' => '', - ) ); - } # end if $_REQUEST['dupID'] + } # end if $_REQUEST['dupID'] + $monitor->Name(translate('Monitor').'-'.$nextId); + $monitor->WebColour(random_colour()); } # end if $_REQUEST['mid'] if ( ZM_OPT_X10 && empty($x10Monitor) ) { @@ -142,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']); @@ -150,8 +75,6 @@ if ( isset($_REQUEST['newMonitor']) ) { if ( ZM_OPT_X10 ) $newX10Monitor = $_REQUEST['newX10Monitor']; } else { - # FIXME: Triggers in the db is a comma separated string. Needs to be an array. - #$monitor->Triggers()= explode( ',', isset($monitor->Triggers())?$monitor->Triggers:"" ); if ( ZM_OPT_X10 ) $newX10Monitor = $x10Monitor; } @@ -168,8 +91,9 @@ if ( !empty($_REQUEST['preset']) ) { $preset = dbFetchOne( 'SELECT Type, Device, Channel, Format, Protocol, Method, Host, Port, Path, Width, Height, Palette, MaxFPS, Controllable, ControlId, ControlDevice, ControlAddress, DefaultRate, DefaultScale FROM MonitorPresets WHERE Id = ?', NULL, array($_REQUEST['preset']) ); foreach ( $preset as $name=>$value ) { # Does isset handle NULL's? I don't think this code is correct. + # Icon: It does, but this means we can't set a null value. if ( isset($value) ) { - $monitor->$name = $value; + $monitor->$name($value); } } } # end if preset @@ -199,7 +123,8 @@ $sourceTypes = array( 'Libvlc' => translate('Libvlc'), 'cURL' => 'cURL (HTTP(S) only)', 'WebSite'=> 'Web Site', - 'NVSocket' => translate('NVSocket') + 'NVSocket' => translate('NVSocket'), + 'VNC' => translate('VNC'), ); if ( !ZM_HAS_V4L ) unset($sourceTypes['Local']); @@ -246,14 +171,14 @@ unset($httpMethods['jpegTags']); if ( ZM_HAS_V4L1 ) { $v4l1DeviceFormats = array( - 'PAL' => 0, - 'NTSC' => 1, - 'SECAM' => 2, - 'AUTO' => 3, - 'FMT4' => 4, - 'FMT5' => 5, - 'FMT6' => 6, - 'FMT7' => 7 + 0 => 'PAL', + 1 => 'NTSC', + 2 => 'SECAM', + 3 => 'AUTO', + 4 => 'FMT4', + 5 => 'FMT5', + 6 => 'FMT6', + 7 => 'FMT7' ); $v4l1MaxChannels = 15; @@ -262,48 +187,48 @@ if ( ZM_HAS_V4L1 ) { $v4l1DeviceChannels[$i] = $i; $v4l1LocalPalettes = array( - translate('Grey') => 1, - 'BGR32' => 5, - 'BGR24' => 4, - '*YUYV' => 8, - '*RGB565' => 3, - '*RGB555' => 6, - '*YUV422' => 7, - '*YUV422P' => 13, - '*YUV420P' => 15 + 1 => translate('Grey'), + 5 => 'BGR32', + 4 => 'BGR24', + 8 => '*YUYV', + 3 => '*RGB565', + 6 => '*RGB555', + 7 => '*YUV422', + 13 => '*YUV422P', + 15 => '*YUV420P', ); } if ( ZM_HAS_V4L2 ) { $v4l2DeviceFormats = array( - 'PAL' => 0x000000ff, - 'NTSC' => 0x0000b000, - 'PAL B' => 0x00000001, - 'PAL B1' => 0x00000002, - 'PAL G' => 0x00000004, - 'PAL H' => 0x00000008, - 'PAL I' => 0x00000010, - 'PAL D' => 0x00000020, - 'PAL D1' => 0x00000040, - 'PAL K' => 0x00000080, - 'PAL M' => 0x00000100, - 'PAL N' => 0x00000200, - 'PAL Nc' => 0x00000400, - 'PAL 60' => 0x00000800, - 'NTSC M' => 0x00001000, - 'NTSC M JP' => 0x00002000, - 'NTSC 443' => 0x00004000, - 'NTSC M KR' => 0x00008000, - 'SECAM B' => 0x00010000, - 'SECAM D' => 0x00020000, - 'SECAM G' => 0x00040000, - 'SECAM H' => 0x00080000, - 'SECAM K' => 0x00100000, - 'SECAM K1' => 0x00200000, - 'SECAM L' => 0x00400000, - 'SECAM LC' => 0x00800000, - 'ATSC 8 VSB' => 0x01000000, - 'ATSC 16 VSB' => 0x02000000, + 0x000000ff => 'PAL', + 0x0000b000 => 'NTSC', + 0x00000001 => 'PAL B', + 0x00000002 => 'PAL B1', + 0x00000004 => 'PAL G', + 0x00000008 => 'PAL H', + 0x00000010 => 'PAL I', + 0x00000020 => 'PAL D', + 0x00000040 => 'PAL D1', + 0x00000080 => 'PAL K', + 0x00000100 => 'PAL M', + 0x00000200 => 'PAL N', + 0x00000400 => 'PAL Nc', + 0x00000800 => 'PAL 60', + 0x00001000 => 'NTSC M', + 0x00002000 => 'NTSC M JP', + 0x00004000 => 'NTSC 443', + 0x00008000 => 'NTSC M KR', + 0x00010000 => 'SECAM B', + 0x00020000 => 'SECAM D', + 0x00040000 => 'SECAM G', + 0x00080000 => 'SECAM H', + 0x00100000 => 'SECAM K', + 0x00200000 => 'SECAM K1', + 0x00400000 => 'SECAM L', + 0x00800000 => 'SECAM LC', + 0x01000000 => 'ATSC 8 VSB', + 0x02000000 => 'ATSC 16 VSB', ); $v4l2MaxChannels = 31; @@ -312,57 +237,58 @@ if ( ZM_HAS_V4L2 ) { $v4l2DeviceChannels[$i] = $i; $v4l2LocalPalettes = array( - 'Auto' => 0, /* Automatic palette selection */ + 0 => 'Auto', /* Automatic palette selection */ - /* Pixel format FOURCC depth Description */ - translate('Grey') => fourcc('G','R','E','Y'), /* 8 Greyscale */ - 'BGR32' => fourcc('B','G','R','4'), /* 32 BGR-8-8-8-8 */ - 'RGB32' => fourcc('R','G','B','4'), /* 32 RGB-8-8-8-8 */ - 'BGR24' => fourcc('B','G','R','3'), /* 24 BGR-8-8-8 */ - 'RGB24' => fourcc('R','G','B','3'), /* 24 RGB-8-8-8 */ - '*YUYV' => fourcc('Y','U','Y','V'), /* 16 YUV 4:2:2 */ + /* FOURCC => Pixel format depth Description */ + fourcc('G','R','E','Y') => translate('Grey'), /* 8 Greyscale */ + fourcc('B','G','R','4') => 'BGR32', /* 32 BGR-8-8-8-8 */ + fourcc('R','G','B','4') => 'RGB32', /* 32 RGB-8-8-8-8 */ + fourcc('B','G','R','3') => 'BGR24', /* 24 BGR-8-8-8 */ + fourcc('R','G','B','3') => 'RGB24', /* 24 RGB-8-8-8 */ + fourcc('Y','U','Y','V') => '*YUYV', /* 16 YUV 4:2:2 */ /* compressed formats */ - '*JPEG' => fourcc('J','P','E','G'), /* JFIF JPEG */ - '*MJPEG' => fourcc('M','J','P','G'), /* Motion-JPEG */ - //'DV' => fourcc('d','v','s','d'), /* 1394 */ - //'MPEG' => fourcc('M','P','E','G'), /* MPEG-1/2/4 */ + fourcc('J','P','E','G') => '*JPEG', /* JFIF JPEG */ + fourcc('M','J','P','G') => '*MJPEG', /* Motion-JPEG */ + // fourcc('d','v','s','d') => 'DV', /* 1394 */ + // fourcc('M','P','E','G') => 'MPEG', /* MPEG-1/2/4 */ - //'RGB332' => fourcc('R','G','B','1'), /* 8 RGB-3-3-2 */ - '*RGB444' => fourcc('R','4','4','4'), /* 16 xxxxrrrr ggggbbbb */ - '*RGB555' => fourcc('R','G','B','O'), /* 16 RGB-5-5-5 */ - '*RGB565' => fourcc('R','G','B','P'), /* 16 RGB-5-6-5 */ - //'RGB555X' => fourcc('R','G','B','Q'), /* 16 RGB-5-5-5 BE */ - //'RGB565X' => fourcc('R','G','B','R'), /* 16 RGB-5-6-5 BE */ - //'Y16' => fourcc('Y','1','6',''), /* 16 Greyscale */ - //'PAL8' => fourcc('P','A','L','8'), /* 8 8-bit palette */ - //'YVU410' => fourcc('Y','V','U','9'), /* 9 YVU 4:1:0 */ - //'YVU420' => fourcc('Y','V','1','2'), /* 12 YVU 4:2:0 */ + // + fourcc('R','G','B','1') => 'RGB332', /* 8 RGB-3-3-2 */ + fourcc('R','4','4','4') => '*RGB444', /* 16 xxxxrrrr ggggbbbb */ + fourcc('R','G','B','O') => '*RGB555', /* 16 RGB-5-5-5 */ + fourcc('R','G','B','P') => '*RGB565', /* 16 RGB-5-6-5 */ + // fourcc('R','G','B','Q') => 'RGB555X', /* 16 RGB-5-5-5 BE */ + // fourcc('R','G','B','R') => 'RGB565X', /* 16 RGB-5-6-5 BE */ + // fourcc('Y','1','6','') => 'Y16', /* 16 Greyscale */ + // fourcc('P','A','L','8') => 'PAL8', /* 8 8-bit palette */ + // fourcc('Y','V','U','9') => 'YVU410', /* 9 YVU 4:1:0 */ + // fourcc('Y','V','1','2') => 'YVU420', /* 12 YVU 4:2:0 */ - '*UYVY' => fourcc('U','Y','V','Y'), /* 16 YUV 4:2:2 */ - '*YUV422P' => fourcc('4','2','2','P'), /* 16 YVU422 planar */ - '*YUV411P' => fourcc('4','1','1','P'), /* 16 YVU411 planar */ - //'Y41P' => fourcc('Y','4','1','P'), /* 12 YUV 4:1:1 */ - '*YUV444' => fourcc('Y','4','4','4'), /* 16 xxxxyyyy uuuuvvvv */ - //'YUV555' => fourcc('Y','U','V','O'), /* 16 YUV-5-5-5 */ - //'YUV565' => fourcc('Y','U','V','P'), /* 16 YUV-5-6-5 */ - //'YUV32' => fourcc('Y','U','V','4'), /* 32 YUV-8-8-8-8 */ + fourcc('U','Y','V','Y') => '*UYVY', /* 16 YUV 4:2:2 */ + fourcc('4','2','2','P') => '*YUV422P', /* 16 YVU422 planar */ + fourcc('4','1','1','P') => '*YUV411P', /* 16 YVU411 planar */ + // fourcc('Y','4','1','P') => 'Y41P', /* 12 YUV 4:1:1 */ + fourcc('Y','4','4','4') => '*YUV444', /* 16 xxxxyyyy uuuuvvvv */ + // fourcc('Y','U','V','O') => 'YUV555', /* 16 YUV-5-5-5 */ + // fourcc('Y','U','V','P') => 'YUV565', /* 16 YUV-5-6-5 */ + // fourcc('Y','U','V','4') => 'YUV32', /* 32 YUV-8-8-8-8 */ /* two planes -- one Y, one Cr + Cb interleaved */ - //'NV12' => fourcc('N','V','1','2'), /* 12 Y/CbCr 4:2:0 */ - //'NV21' => fourcc('N','V','2','1'), /* 12 Y/CrCb 4:2:0 */ + fourcc('N','V','1','2') => 'NV12', /* 12 Y/CbCr 4:2:0 */ + // fourcc('N','V','2','1') => 'NV21', /* 12 Y/CrCb 4:2:0 */ /* The following formats are not defined in the V4L2 specification */ - '*YUV410' => fourcc('Y','U','V','9'), /* 9 YUV 4:1:0 */ - '*YUV420' => fourcc('Y','U','1','2'), /* 12 YUV 4:2:0 */ - //'YYUV' => fourcc('Y','Y','U','V'), /* 16 YUV 4:2:2 */ - //'HI240' => fourcc('H','I','2','4'), /* 8 8-bit color */ - //'HM12' => fourcc('H','M','1','2'), /* 8 YUV 4:2:0 16x16 macroblocks */ + fourcc('Y','U','V','9') => '*YUV410', /* 9 YUV 4:1:0 */ + fourcc('Y','U','1','2') => '*YUV420', /* 12 YUV 4:2:0 */ + // fourcc('Y','Y','U','V') => 'YYUV', /* 16 YUV 4:2:2 */ + // fourcc('H','I','2','4') => 'HI240', /* 8 8-bit color */ + // fourcc('H','M','1','2') => 'HM12', /* 8 YUV 4:2:0 16x16 macroblocks */ /* see http://www.siliconimaging.com/RGB%20Bayer.htm */ - //'SBGGR8' => fourcc('B','A','8','1'), /* 8 BGBG.. GRGR.. */ - //'SGBRG8' => fourcc('G','B','R','G'), /* 8 GBGB.. RGRG.. */ - //'SBGGR16' => fourcc('B','Y','R','2'), /* 16 BGBG.. GRGR.. */ + // fourcc('B','A','8','1') => 'SBGGR8', /* 8 BGBG.. GRGR.. */ + // fourcc('G','B','R','G') => 'SGBRG8', /* 8 GBGB.. RGRG.. */ + // fourcc('B','Y','R','2') => 'SBGGR16', /* 16 BGBG.. GRGR.. */ /* Vendor-specific formats */ //'WNVA' => fourcc('W','N','V','A'), /* Winnov hw compress */ @@ -387,71 +313,64 @@ $Colours = array( ); $orientations = array( - '0' => translate('Normal'), - '90' => translate('RotateRight'), - '180' => translate('Inverted'), - '270' => translate('RotateLeft'), - 'hori' => translate('FlippedHori'), - 'vert' => translate('FlippedVert') + 'ROTATE_0' => translate('Normal'), + 'ROTATE_90' => translate('RotateRight'), + 'ROTATE_180' => translate('Inverted'), + 'ROTATE_270' => translate('RotateLeft'), + 'FLIP_HORI' => translate('FlippedHori'), + 'FLIP_VERT' => translate('FlippedVert') ); $deinterlaceopts = array( - 'Disabled' => 0x00000000, - 'Four field motion adaptive - Soft' => 0x00001E04, /* 30 change */ - 'Four field motion adaptive - Medium' => 0x00001404, /* 20 change */ - 'Four field motion adaptive - Hard' => 0x00000A04, /* 10 change */ - 'Discard' => 0x00000001, - 'Linear' => 0x00000002, - 'Blend' => 0x00000003, - 'Blend (25%)' => 0x00000205 - ); + 0x00000000 => 'Disabled', + 0x00001E04 => 'Four field motion adaptive - Soft', /* 30 change */ + 0x00001404 => 'Four field motion adaptive - Medium', /* 20 change */ + 0x00000A04 => 'Four field motion adaptive - Hard', /* 10 change */ + 0x00000001 => 'Discard', + 0x00000002 => 'Linear', + 0x00000003 => 'Blend', + 0x00000205 => 'Blend (25%)', +); $deinterlaceopts_v4l2 = array( - 'Disabled' => 0x00000000, - 'Four field motion adaptive - Soft' => 0x00001E04, /* 30 change */ - 'Four field motion adaptive - Medium' => 0x00001404, /* 20 change */ - 'Four field motion adaptive - Hard' => 0x00000A04, /* 10 change */ - 'Discard' => 0x00000001, - 'Linear' => 0x00000002, - 'Blend' => 0x00000003, - 'Blend (25%)' => 0x00000205, - 'V4L2: Capture top field only' => 0x02000000, - 'V4L2: Capture bottom field only' => 0x03000000, - 'V4L2: Alternate fields (Bob)' => 0x07000000, - 'V4L2: Progressive' => 0x01000000, - 'V4L2: Interlaced' => 0x04000000 - ); + 0x00000000 => 'Disabled', + 0x00001E04 => 'Four field motion adaptive - Soft', /* 30 change */ + 0x00001404 => 'Four field motion adaptive - Medium', /* 20 change */ + 0x00000A04 => 'Four field motion adaptive - Hard', /* 10 change */ + 0x00000001 => 'Discard', + 0x00000002 => 'Linear', + 0x00000003 => 'Blend', + 0x00000205 => 'Blend (25%)', + 0x02000000 => 'V4L2: Capture top field only', + 0x03000000 => 'V4L2: Capture bottom field only', + 0x07000000 => 'V4L2: Alternate fields (Bob)', + 0x01000000 => 'V4L2: Progressive', + 0x04000000 => 'V4L2: Interlaced', +); $fastblendopts = array( - 'No blending' => 0, - '1.5625%' => 1, - '3.125%' => 3, - '6.25% (Indoor)' => 6, - '12.5% (Outdoor)' => 12, - '25%' => 25, - '50%' => 50 + 0 => 'No blending', + 1 => '1.5625%', + 3 => '3.125%', + 6 => '6.25% (Indoor)', + 12 => '12.5% (Outdoor)', + 25 => '25%', + 50 => '50%', ); $fastblendopts_alarm = array( - 'No blending (Alarm lasts forever)' => 0, - '1.5625%' => 1, - '3.125%' => 3, - '6.25%' => 6, - '12.5%' => 12, - '25%' => 25, - '50% (Alarm lasts a moment)' => 50 + 0 => 'No blending (Alarm lasts forever)', + 1 => '1.5625%', + 3 => '3.125%', + 6 => '6.25%', + 12 => '12.5%', + 25 => '25%', + 50 => '50% (Alarm lasts a moment)', ); $label_size = array( - 'Default' => 1, - 'Large' => 2 - ); - -$savejpegopts = array( - 'Disabled' => 0, - 'Frames only' => 1, - 'Analysis images only (if available)' => 2, - 'Frames + Analysis images (if available)' => 3, + 1 => translate('Default'), + 2 => translate('Large'), ); $codecs = array( @@ -460,41 +379,22 @@ $codecs = array( 'MJPEG' => translate('MJPEG'), ); +$controls = ZM\Control::find(null, array('order'=>'lower(Name)')); + xhtmlHeaders(__FILE__, translate('Monitor').' - '.validHtmlStr($monitor->Name())); getBodyTopHTML(); +echo getNavBarHTML(); ?> -
- -
-
    +
    + +
    + + +
    + +
    +
    + + +
    + +

    - Name()) ?>Id() ) { ?> (Id()?>)

    - - - +
    + Configuration cloned from Monitor: +
    GroupIds() as $group_id ) { -echo ''; -} -?> - - - - - - - - -Triggers() ) { - foreach( explode(',', $monitor->Triggers()) as $newTrigger ) { -?> - -Type() != 'Local') ) { ?> - - - - - - +
    + + + +
    Type()!= 'Remote' ) { +} // end if canEdit('Monitors') ?> - - - +
    + + +
    + + + + +
    Type()!= 'Local' && $monitor->Type()!= 'Remote' && $monitor->Type()!= 'Ffmpeg' && $monitor->Type()!= 'Libvlc') ) { +foreach ( $tabs as $name=>$value ) { + echo '
    '; ?> - -Type()!= 'Ffmpeg' && $monitor->Type()!= 'Libvlc' )) { -?> - -Type()!= 'Remote' && $monitor->Type()!= 'File' && $monitor->Type()!= 'Ffmpeg' && $monitor->Type()!= 'Libvlc' && $monitor->Type()!= 'cURL' && $monitor->Type() != 'WebSite') ) { -?> - - - - - - - - - - - - - - - - - -Type() != 'Remote' && $monitor->Protocol()!= 'rtsp') ) { -?> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
    - - + + + + + + + + + - + + + + + + + + - - - -Type != 'WebSite' ) { -?> - - - - - + + + + +Type() != 'WebSite' ) { +?> + + + + + + + + + + + + Type() != 'Local' && $monitor->Type() != 'File' && $monitor->Type() != 'NVSocket' ) { ?> - + - + - - + + + + + + + + - - + + - - + + + - - + + - + + + + + + + + + + + + + + + + +Type() == 'Local' ) { ?> - - + + + + + Method() == 'v4l1' ) { ?> - - + + + + + + + + + + - - - - - + + + + + + + + + + + + - - + + + + Type() == 'NVSocket' ) { include('_monitor_source_nvsocket.php'); + } else if ( $monitor->Type() == 'VNC' ) { +?> + + + + + + + + + + + + + + + + +Type() == 'Remote' ) { ?> - + + + + + + + -Method()); } else { -?> - -Method()); } ?> - - - + + + + + Type() == 'File' ) { ?> - + Type() == 'cURL' ) { ?> - - - + + + Type() == 'WebSite' ) { ?> - - - - + + + + + + + + + + + + + + + + Type() == 'Ffmpeg' || $monitor->Type() == 'Libvlc' ) { + } else if ( $monitor->Type() == 'Ffmpeg' || $monitor->Type() == 'Libvlc' ) { ?> - - + + + + + + + + - + Type() == 'Ffmpeg' ) { ?> - - Type() != 'NVSocket' && $monitor->Type() != 'WebSite' ) { + if ( $monitor->Type() != 'NVSocket' && $monitor->Type() != 'WebSite' ) { ?> - - - - - + + + + + + + + + + + + + + + + Type() == 'Local' ) { + if ( $monitor->Type() == 'Local' ) { ?> - - + + + +Type() != 'WebSite' ) { ?> - + + + + - Type() == 'Remote' ) { ?> - Protocol()!= 'rtsp' ) { echo ' style="display:none;"'; } ?>> + Protocol()!= 'rtsp' ) { echo ' style="display:none;"'; } ?>> + + + Type() == 'Remote' break; } case 'storage' : ?> - - + + + + + + + + @@ -1007,8 +961,13 @@ echo htmlSelect('newMonitor[OutputContainer]', $videowriter_containers, $monitor ?> - - + + + + - - - + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - +echo htmlSelect('newMonitor[ReturnLocation]', $return_options, $monitor->ReturnLocation()); ?> + + + + + - - - + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
    +
    'None','auto'=>'Auto'); foreach ( ZM\Server::find(NULL, array('order'=>'lower(Name)')) as $Server ) { @@ -696,206 +493,297 @@ switch ( $tab ) { } echo htmlSelect( 'newMonitor[ServerId]', $servers, $monitor->ServerId() ); ?> -
    +
    Type()); ?>
    'Default'); - foreach ( ZM\Storage::find( NULL, array('order'=>'lower(Name)') ) as $Storage ) { - $storage_areas[$Storage->Id()] = $Storage->Name(); + $function_options = array(); + foreach ( getEnumValues('Monitors', 'Function') as $f ) { + $function_options[$f] = translate("Fn$f"); } - echo htmlSelect( 'newMonitor[StorageId]', $storage_areas, $monitor->StorageId() ); + echo htmlSelect('newMonitor[Function]', $function_options, $monitor->Function()); ?> -
    Type()); ?>
    Enabled() ) { ?> checked="checked"/>
     () -
    Enabled() ? ' checked="checked"' : '' ?>/>
    +Id() || ($monitor->Id()!= $linked_monitor['Id'])) && visibleMonitor($linked_monitor['Id']) ) { + $monitor_options[$linked_monitor['Id']] = validHtmlStr($linked_monitor['Name']); + } + } + + echo htmlSelect( + 'newMonitor[LinkedMonitors][]', + $monitor_options, + ( $monitor->LinkedMonitors() ? explode(',', $monitor->LinkedMonitors()) : array() ), + array('class'=>'chosen','multiple'=>'multiple') + ); +?> +
     () - + CAUTION: See the help text
     () - + CAUTION: See the help text
    RefBlendPerc()); ?>
    -
    AlarmRefBlendPerc()); ?>
    +
    '; + echo 'Triggers()) && in_array($optTrigger, $monitor->Triggers()) ) ? ' checked="checked"' : ''). '/> '. $optTrigger; + $optCount ++; + } # end foreach trigger option + if ( !$optCount ) { + echo ''.translate('NoneAvailable').''; + } ?> - Triggers() ) && in_array( $optTrigger, $monitor->Triggers() ) ) { ?> checked="checked"/>  - - -
    Method(), "submitTab( '$tab' );" ); ?>
    Method(), array('onchange'=>'submitTab', 'data-tab-name'=>$tab) ); ?>
    Channel()); ?>
    Format()); ?>
    Palette()); ?>
    Channel()); ?>
    Format()); ?>
    Palette()); ?>
    +
    V4LMultiBuffer() == '1' ? 'checked="checked"' : '' ) ?>/> V4LMultiBuffer() == '0' ? 'checked="checked"' : '' ) ?>/> - V4LMultiBuffer() ? 'checked="checked"' : '' ) ?>/> + V4LMultiBuffer() == '' ? 'checked="checked"' : '' ) ?>/>
    Protocol(), "updateMethods( this );if(this.value=='rtsp'){\$('RTSPDescribe').setStyle('display','table-row');}else{\$('RTSPDescribe').hide();}" ); ?>
    Protocol(), "updateMethods( this );if(this.value=='rtsp'){\$('RTSPDescribe').setStyle('display','table-row');}else{\$('RTSPDescribe').hide();}" ); ?>
    Protocol() || $monitor->Protocol() == 'http' ) { -?> -
    Method() ); ?>
    Method() ); ?>
    ()
    ()
    ()
    ()
     ()Method() ); ?>
    + Method()) ?>
     (Type()), 'zmOptionHelp', 'optionhelp', '?' ) ?>)Type())) ?>
    - () + +
    - () + +
    Colours() ); ?> -
    ()
    ()
    Orientation() );?>
    Colours()) ?>
    () + + +translate('Custom'), + '176x120'=>'176x120 QCIF', + '176x144'=>'176x14', + '320x240'=>'320x240', + '320x200'=>'320x200', + '352x240'=>'352x240 CIF', + '640x480'=>'640x480', + '640x400'=>'640x400', + '704x240'=>'704x240 2CIF', + '704x480'=>'704x480 4CIF', + '720x480'=>'720x480 D1', + '1280x720'=>'1280x720 720p', + '1280x800'=>'1280x800', + '1280x960'=>'1280x960 960p', + '1280x1024'=>'1280x1024 1MP', + '1600x1200'=>'1600x1200 2MP', + '1920x1080'=>'1920x1080 1080p', + '2048x1536'=>'2048x1536 3MP', + '2592x1944'=>'2592x1944 5MP', + '3840x2160'=>'3840x2160 4K UHD', +), $monitor->Width().'x'.$monitor->Height() +); +?> +
    Orientation());?>
    Deinterlacing())?>
    Deinterlacing())?>
     () RTSPDescribe() ) { ?> checked="checked"/>
    RTSPDescribe() ) { ?> checked="checked"/>
    +
    +'Default'); + foreach ( ZM\Storage::find(array('Enabled'=>true), 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()); +?> +
    'Disabled', ); - if (stripos(php_uname('m'), 'arm') === false ) - $videowriteropts[1] = 'X264 Encode'; - else - $videowriteropts[1] = array('text'=>'X264 Encode - Not compatible on Arm','disabled'=>1); + $videowriteropts[1] = 'X264 Encode'; - if ($monitor->Type() == 'Ffmpeg' ) + if ( $monitor->Type() == 'Ffmpeg' ) $videowriteropts[2] = 'H264 Camera Passthrough'; else $videowriteropts[2] = array('text'=>'H264 Camera Passthrough - only for FFMPEG','disabled'=>1); - echo htmlselect('newMonitor[VideoWriter]', $videowriteropts, $monitor->VideoWriter()); + echo htmlSelect('newMonitor[VideoWriter]', $videowriteropts, $monitor->VideoWriter()); ?>
    +
    + +
    Type() == 'Ffmpeg' ) { ?> RecordAudio() ) { ?> checked="checked"/> @@ -1022,53 +981,125 @@ echo htmlSelect('newMonitor[OutputContainer]', $videowriter_containers, $monitor case 'timestamp' : { ?> -
    LabelSize()) ?>
    ImageBufferCount() * $monitor->Width() * $monitor->Height() * $monitor->Colours(), 0) ?>
    Controllable() ) { ?> checked="checked"/>
     
    TrackMotion() ) { ?> checked="checked"/>
    Controllable() ) { ?> checked="checked"/>
    +translate('None')); + foreach ( $controls as $control ) { + $controlTypes[$control->Id()] = $control->Name(); + } + + echo htmlSelect('newMonitor[ControlId]', $controlTypes, $monitor->ControlId()); + if ( canEdit('Control') ) { + echo ' '.makePopupLink('?view=controlcaps', 'zmControlCaps', 'controlcaps', translate('Edit')); + } +?> +
    TrackMotion() ) { ?> checked="checked"/>
    translate('None'), '0' => translate('Home'), - '1' => translate('Preset')." 1", + '1' => translate('Preset').' 1', ); -?> -
    - +
    - +
    - +
    - +
    - +
    - +
    DefaultRate()); ?>
    DefaultScale()); ?>
    DefaultCodec()); ?>
    - +
    - +     
    - +      + sync +
     () Exif() ) { ?> checked="checked"/>
    -
    - - +
    + +
    +
    + +
    - - +
    +
    + diff --git a/web/skins/classic/views/monitorpreset.php b/web/skins/classic/views/monitorpreset.php index ef5a6ac01..22c960587 100644 --- a/web/skins/classic/views/monitorpreset.php +++ b/web/skins/classic/views/monitorpreset.php @@ -36,10 +36,9 @@ $focusWindow = true; xhtmlHeaders(__FILE__, translate('MonitorPreset') ); ?> +
    -
    @@ -57,5 +56,4 @@ xhtmlHeaders(__FILE__, translate('MonitorPreset') );
    - - + diff --git a/web/skins/classic/views/monitorprobe.php b/web/skins/classic/views/monitorprobe.php index 8fb626516..d670e0fcc 100644 --- a/web/skins/classic/views/monitorprobe.php +++ b/web/skins/classic/views/monitorprobe.php @@ -39,16 +39,18 @@ function probeV4L() { } $monitors = array(); - foreach ( dbFetchAll("SELECT Id, Name, Device,Channel FROM Monitors WHERE Type = 'Local' ORDER BY Device, Channel" ) as $monitor ) + foreach ( dbFetchAll("SELECT Id, Name, Device, Channel FROM Monitors WHERE Type = 'Local' ORDER BY Device, Channel" ) as $monitor ) $monitors[$monitor['Device'].':'.$monitor['Channel']] = $monitor; $devices = array(); $preferredStandards = array('PAL', 'NTSC'); $preferredFormats = array('BGR3', 'RGB3', 'YUYV', 'UYVY', 'JPEG', 'MJPG', '422P', 'YU12', 'GREY'); foreach ( $output as $line ) { - if ( !preg_match('/^d:([^|]+).*S:([^|]*).*F:([^|]+).*I:(\d+)\|(.+)$/', $line, $deviceMatches) ) - ZM\Fatal("Can't parse command output '$line'"); - $standards = explode('/',$deviceMatches[2]); + if ( !preg_match('/^d:([^|]+).*S:([^|]*).*F:([^|]+).*I:(\d+)\|(.+)$/', $line, $deviceMatches) ) { + ZM\Error("Can't parse command output '$line'"); + continue; + } + $standards = explode('/', $deviceMatches[2]); $preferredStandard = false; foreach ( $preferredStandards as $standard ) { if ( in_array( $standard, $standards ) ) { @@ -56,7 +58,7 @@ function probeV4L() { break; } } - $formats = explode('/',$deviceMatches[3]); + $formats = explode('/', $deviceMatches[3]); $preferredFormat = false; foreach ( $preferredFormats as $format ) { if ( in_array($format, $formats) ) { @@ -73,8 +75,10 @@ function probeV4L() { ); $inputs = array(); for ( $i = 0; $i < $deviceMatches[4]; $i++ ) { - if ( !preg_match('/i'.$i.':([^|]+)\|i'.$i.'T:([^|]+)\|/', $deviceMatches[5], $inputMatches) ) - ZM\Fatal("Can't parse input '".$deviceMatches[5]."'"); + if ( !preg_match('/i'.$i.':([^|]+)\|i'.$i.'T:([^|]+)\|/', $deviceMatches[5], $inputMatches) ) { + ZM\Error("Can't parse input '".$deviceMatches[5]."'"); + continue; + } if ( $inputMatches[2] == 'Camera' ) { $input = array( 'index' => $i, @@ -101,7 +105,7 @@ function probeV4L() { $inputMonitor['Colours'] = 1; $inputMonitor['SignalCheckColour'] = '#000023'; } - $inputDesc = base64_encode(serialize($inputMonitor)); + $inputDesc = base64_encode(json_encode($inputMonitor)); $inputString = $deviceMatches[1].', chan '.$i.($input['free']?(' - '.translate('Available')):(' ('.$monitors[$input['id']]['Name'].')')); $inputs[] = $input; $cameras[$inputDesc] = $inputString; @@ -247,18 +251,18 @@ function probeNetwork() { $arp_command = ZM_PATH_ARP; $result = explode(' ', $arp_command); if ( !is_executable($result[0]) ) { - ZM\Error("ARP compatible binary not found or not executable by the web user account. Verify ZM_PATH_ARP points to a valid arp tool."); - return; + ZM\Error('ARP compatible binary not found or not executable by the web user account. Verify ZM_PATH_ARP points to a valid arp tool.'); + return $cameras; } $result = exec(escapeshellcmd($arp_command), $output, $status); if ( $status ) { ZM\Error("Unable to probe network cameras, status is '$status'"); - return; + return $cameras; } $monitors = array(); - foreach ( dbFetchAll("SELECT Id, Name, Host FROM Monitors WHERE Type = 'Remote' ORDER BY Host") as $monitor ) { + foreach ( dbFetchAll("SELECT `Id`, `Name`, `Host` FROM `Monitors` WHERE `Type` = 'Remote' ORDER BY `Host`") as $monitor ) { if ( preg_match('/^(.+)@(.+)$/', $monitor['Host'], $matches) ) { //echo "1: ".$matches[2]." = ".gethostbyname($matches[2])."
    "; $monitors[gethostbyname($matches[2])] = $monitor; @@ -277,29 +281,29 @@ function probeNetwork() { '78:a5:dd' => array('type'=>'Wansview','probeFunc'=>'probeWansview') ); - foreach ( $output as $line ) { - if ( !preg_match('/(\d+\.\d+\.\d+\.\d+).*(([0-9a-f]{2}:){5})/', $line, $matches) ) - continue; - $ip = $matches[1]; - $host = $ip; - $mac = $matches[2]; - //echo "I:$ip, H:$host, M:$mac
    "; - $macRoot = substr($mac,0,8); - if ( isset($macBases[$macRoot]) ) { - $macBase = $macBases[$macRoot]; - $camera = call_user_func($macBase['probeFunc'], $ip); - $sourceDesc = base64_encode(serialize($camera['monitor'])); - $sourceString = $camera['model'].' @ '.$host; - if ( isset($monitors[$ip]) ) { - $monitor = $monitors[$ip]; - $sourceString .= ' ('.$monitor['Name'].')'; - } else { - $sourceString .= ' - '.translate('Available'); - } - $cameras[$sourceDesc] = $sourceString; + foreach ( $output as $line ) { + if ( !preg_match('/(\d+\.\d+\.\d+\.\d+).*(([0-9a-f]{2}:){5})/', $line, $matches) ) + continue; + $ip = $matches[1]; + $host = $ip; + $mac = $matches[2]; + //echo "I:$ip, H:$host, M:$mac
    "; + $macRoot = substr($mac,0,8); + if ( isset($macBases[$macRoot]) ) { + $macBase = $macBases[$macRoot]; + $camera = call_user_func($macBase['probeFunc'], $ip); + $sourceDesc = base64_encode(json_encode($camera['monitor'])); + $sourceString = $camera['model'].' @ '.$host; + if ( isset($monitors[$ip]) ) { + $monitor = $monitors[$ip]; + $sourceString .= ' ('.$monitor['Name'].')'; + } else { + $sourceString .= ' - '.translate('Available'); } - } # end foreach output line - return $cameras; + $cameras[$sourceDesc] = $sourceString; + } + } # end foreach output line + return $cameras; } # end function probeNetwork() $cameras = array(); @@ -317,10 +321,9 @@ $focusWindow = true; xhtmlHeaders(__FILE__, translate('MonitorProbe') ); ?> +
    - +

    @@ -330,7 +333,7 @@ xhtmlHeaders(__FILE__, translate('MonitorProbe') );

    - + 'configureButtons(this)')); ?>

    - - + diff --git a/web/skins/classic/views/montage.php b/web/skins/classic/views/montage.php index d8bd27ae6..1c62fddf4 100644 --- a/web/skins/classic/views/montage.php +++ b/web/skins/classic/views/montage.php @@ -33,46 +33,43 @@ if ( isset($_REQUEST['showZones']) ) { } } $widths = array( - 'auto' => 'auto', - '160px' => '160px', - '320px' => '320px', - '352px' => '352px', - '640px' => '640px', - '1280px' => '1280px' ); + '0' => 'auto', + '160' => '160px', + '320' => '320px', + '352' => '352px', + '640' => '640px', + '1280' => '1280px' ); $heights = array( - 'auto' => 'auto', - '240px' => '240px', - '320px' => '320px', - '480px' => '480px', - '720px' => '720px', - '1080px' => '1080px', + '0' => 'auto', + '240' => '240px', + '320' => '320px', + '480' => '480px', + '720' => '720px', + '1080' => '1080px', ); -$scale = '100'; # actual - -if ( isset( $_REQUEST['scale'] ) ) { - $scale = validInt($_REQUEST['scale']); -} else if ( isset($_COOKIE['zmMontageScale']) ) { - $scale = $_COOKIE['zmMontageScale']; -} - -if ( ! $scale ) - $scale = 100; $layouts = ZM\MontageLayout::find(NULL, array('order'=>"lower('Name')")); $layoutsById = array(); foreach ( $layouts as $l ) { - $layoutsById[$l->Id()] = $l; + if ( $l->Name() == 'Freeform' ) { + $layoutsById[$l->Id()] = $l; + break; + } +} +foreach ( $layouts as $l ) { + if ( $l->Name() != 'Freeform' ) + $layoutsById[$l->Id()] = $l; } -session_start(); +zm_session_start(); $layout_id = ''; if ( isset($_COOKIE['zmMontageLayout']) ) { $layout_id = $_SESSION['zmMontageLayout'] = $_COOKIE['zmMontageLayout']; -#} elseif ( isset($_SESSION['zmMontageLayout']) ) { - #$layout_id = $_SESSION['zmMontageLayout']; +} elseif ( isset($_SESSION['zmMontageLayout']) ) { + $layout_id = $_SESSION['zmMontageLayout']; } $options = array(); @@ -81,27 +78,37 @@ $Positions = ''; if ( $layout_id and is_numeric($layout_id) and isset($layoutsById[$layout_id]) ) { $Layout = $layoutsById[$layout_id]; $Positions = json_decode($Layout->Positions(), true); +} else { + ZM\Logger::Debug('Layout not found'); } if ( $Layout and ( $Layout->Name() != 'Freeform' ) ) { // Use layout instead of other options } -if ( isset($_COOKIE['zmMontageWidth']) and $_COOKIE['zmMontageWidth'] ) { - $_SESSION['zmMontageWidth'] = $options['width'] = $_COOKIE['zmMontageWidth']; +if ( isset($_COOKIE['zmMontageWidth']) ) { + $_SESSION['zmMontageWidth'] = $options['width'] = validInt($_COOKIE['zmMontageWidth']); #} elseif ( isset($_SESSION['zmMontageWidth']) and $_SESSION['zmMontageWidth'] ) { #$options['width'] = $_SESSION['zmMontageWidth']; -} else - $options['width'] = ''; +} else { + $options['width'] = 0; +} -if ( isset($_COOKIE['zmMontageHeight']) and $_COOKIE['zmMontageHeight'] ) - $_SESSION['zmMontageHeight'] = $options['height'] = $_COOKIE['zmMontageHeight']; +if ( isset($_COOKIE['zmMontageHeight']) ) { + $_SESSION['zmMontageHeight'] = $options['height'] = validInt($_COOKIE['zmMontageHeight']); #else if ( isset($_SESSION['zmMontageHeight']) and $_SESSION['zmMontageHeight'] ) #$options['height'] = $_SESSION['zmMontageHeight']; -else - $options['height'] = ''; +} else { + $options['height'] = 0; +} -if ( $scale ) - $options['scale'] = $scale; +$scale = '100'; # actual + +if ( isset($_REQUEST['scale']) ) { + $scale = validInt($_REQUEST['scale']); +} else if ( isset($_COOKIE['zmMontageScale']) ) { + $scale = validInt($_COOKIE['zmMontageScale']); +} +$options['scale'] = $scale; session_write_close(); @@ -111,21 +118,20 @@ $filterbar = ob_get_contents(); ob_end_clean(); $monitors = array(); -foreach( $displayMonitors as &$row ) { +foreach ( $displayMonitors as &$row ) { if ( $row['Function'] == 'None' ) continue; $row['Scale'] = $scale; - $row['PopupScale'] = reScale( SCALE_BASE, $row['DefaultScale'], ZM_WEB_DEFAULT_SCALE ); + $row['PopupScale'] = reScale(SCALE_BASE, $row['DefaultScale'], ZM_WEB_DEFAULT_SCALE); if ( ZM_OPT_CONTROL && $row['ControlId'] && $row['Controllable'] ) $showControl = true; - $row['connKey'] = generateConnKey(); if ( ! isset($widths[$row['Width']]) ) { - $widths[$row['Width'].'px'] = $row['Width'].'px'; + $widths[$row['Width']] = $row['Width'].'px'; } if ( ! isset($heights[$row['Height']]) ) { - $heights[$row['Height'].'px'] = $row['Height'].'px'; + $heights[$row['Height']] = $row['Height'].'px'; } $monitors[] = new ZM\Monitor($row); } # end foreach Monitor @@ -135,9 +141,14 @@ xhtmlHeaders(__FILE__, translate('Montage'));
    -