diff --git a/.eslintignore b/.eslintignore index 682db9004..4682851df 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,19 @@ +*.min.js + +# libraries web/api/lib -web/skins/classic/js/jquery-1.11.3.js +web/includes/csrf/ +web/js/videojs.zoomrotate.js +web/skins/classic/js/bootstrap.js +web/skins/classic/js/chosen +web/skins/classic/js/dateTimePicker +web/skins/classic/js/jquery-*.js +web/skins/classic/js/jquery-ui-* web/skins/classic/js/jquery.js +web/skins/classic/js/moment.js +web/skins/classic/js/video.js web/tools/mootools + +# Cannot be parsed as JS +web/skins/classic/includes/export_functions.php +web/skins/classic/views/events.php diff --git a/.eslintrc.js b/.eslintrc.js index fa4d6b6e5..64950ee0d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,25 +5,46 @@ module.exports = { "browser": true, }, "extends": ["google"], + "overrides": [{ + // eslint-plugin-html handles eol-last slightly different - it applies to + // each set of script tags, so we turn it off here. + "files": "**/*.*php", + "rules": { + "eol-last": "off", + "indent": "off", + }, + }], + "plugins": [ + "html", + "php-markup", + ], "rules": { - "brace-style": "off", "camelcase": "off", "comma-dangle": "off", - "key-spacing": "off", + "guard-for-in": "off", "max-len": "off", "new-cap": ["error", { capIsNewExceptions: ["Error", "Warning", "Debug", "Polygon_calcArea", "Play", "Stop"], newIsCapExceptionPattern: "^Asset\.." }], "no-array-constructor": "off", - "no-caller": "off", - "no-new-object": "off", - "no-unused-vars": "off", + "no-unused-vars": ["error", { + "vars": "local", + "args": "none", + "ignoreRestSiblings": false + }], "no-var": "off", - "object-curly-spacing": "off", "prefer-rest-params": "off", "quotes": "off", "require-jsdoc": "off", "spaced-comment": "off", }, + "settings": { + "php/php-extensions": [".php"], + "php/markup-replacement": {"php": "", "=": "0"}, + "php/keep-eol": false, + "php/remove-whitespace": false, + "php/remove-empty-line": false, + "php/remove-php-lint": false + }, }; 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 5f80fe591..610480e8e 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,23 +1,46 @@ -You should only file an issue if you found a bug. Feature and enhancement requests, general discussions and support questions should occur in one of the following areas: +**THIS FORUM IS FOR BUG REPORTS ONLY** -- The ZoneMinder IRC channel - irc.freenode.net #zoneminder +Do not post feature or enhancement requests, general discussions, or support questions here. + +Feature and enhancement requests, general discussions, and support questions should occur in one of the following areas: + +- The [ZoneMinder-Chat Slack channel](https://zoneminder-chat.herokuapp.com/) - The [ZoneMinder Forum](https://forums.zoneminder.com/) -**Do not post feature or enhancement requests, general discussions or support questions here.** - Docker related issues should be posted here: https://github.com/ZoneMinder/zmdockerfiles -Make sure you are running the latest version of ZoneMinder before reporting an issue. +In order to submit a bug report, please populate the fields below this line. This is required. -**ZoneMinder Version (`zmaudit.pl -v`):** +---------------------------------------------------------------------------------------------------- -**Are you using a development snapshot / git checkout? If so, what is the latest commit? (`git rev-parse HEAD`):** +**Describe Your Environment** +- Version of ZoneMinder [release version, development version, or commit] +- How you installed ZoneMinder [e.g. PPA, RPMFusion, from-source, etc] +- Full name and version of OS +- Browser name and version (if this is an issue with the web interface) -**Linux Distribution and Version (`cat /etc/os-release` or `cat /etc/redhat-release`):** +**If the issue concerns a camera** +- Make and Model +- Frame rate +- Resolution +- ZoneMinder Source Type: -**If the issue concerns a camera, provide the make, model, frame rate, resolution and ZoneMinder Source Type:** +**Describe the bug** +A clear and concise description of what the bug is. -**Relevant log lines:** +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Debug Logs** ``` -log lines here + + + ``` diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..b5df0a5ee --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,40 @@ +--- +name: Bug report +about: Issues that do not follow the template will be closed +title: '' +labels: '' +assignees: '' + +--- + +**Describe Your Environment** +- Version of ZoneMinder [release version, development version, or commit] +- How you installed ZoneMinder [e.g. PPA, RPMFusion, from-source, etc] +- Full name and version of OS +- Browser name and version (if this is an issue with the web interface) + +**If the issue concerns a camera** +- Make and Model +- frame rate +- resolution +- ZoneMinder Source Type: + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Debug Logs** +``` + + + +``` diff --git a/.github/config.yml b/.github/config.yml new file mode 100644 index 000000000..543a2c160 --- /dev/null +++ b/.github/config.yml @@ -0,0 +1,21 @@ +# Configuration for welcome - https://github.com/behaviorbot/welcome + +# Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome + +# Comment to be posted to on first time issues +newIssueWelcomeComment: > + Thanks for opening your first issue here! Just a reminder, this forum is for Bug Reports only. Be sure to follow the issue template! + +# Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome + +# Comment to be posted to on PRs from first time contributors in your repository +#newPRWelcomeComment: > +# Thanks for opening this pull request! Please check out our contributing guidelines. + +# Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge + +# Comment to be posted to on pull requests merged by a first time user +#firstPRMergeComment: > +# Congrats on merging your first pull request! We here at behaviorbot are proud of you! + +# It is recommend to include as many gifs and emojis as possible diff --git a/.github/issue-close-app.yml b/.github/issue-close-app.yml new file mode 100644 index 000000000..ebe4cdcfa --- /dev/null +++ b/.github/issue-close-app.yml @@ -0,0 +1,16 @@ +# Comment that will be sent if an issue is judged to be closed +comment: "This issue is closed because it does not meet our bug report issue template. Please read it." +issueConfigs: +# There can be several configs for different kind of issues. +- content: +# Example 1: bug report + - "Describe Your Environment" + - "Describe the bug" + - "Expected behavior" +# Optional configuration: +# +# whether the keywords are case-insensitive +# default value is false, which means keywords are case-sensitive +caseInsensitive: true +# The issue is judged to be legal if it includes all keywords from any of these two configs. +# Or it will be closed by the app. diff --git a/.github/no-response.yml b/.github/no-response.yml new file mode 100644 index 000000000..7e40c036f --- /dev/null +++ b/.github/no-response.yml @@ -0,0 +1,13 @@ +# Configuration for probot-no-response - https://github.com/probot/no-response + +# Number of days of inactivity before an Issue is closed for lack of response +daysUntilClose: 7 +# Label requiring a response +responseRequiredLabel: more-information-needed +# Comment to post when closing an Issue for lack of response. Set to `false` to disable +closeComment: > + This issue has been automatically closed because there has been no response + to our request for more information from the original author. With only the + information that is currently in the issue, we don't have enough information + to take action. Please reach out if you have or find the answers we need so + that we can investigate further. diff --git a/.github/stale.yml b/.github/stale.yml index 971d85ea5..9ff190b1f 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -9,6 +9,9 @@ daysUntilClose: 7 # 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/.github/support.yml b/.github/support.yml new file mode 100644 index 000000000..c62515ee2 --- /dev/null +++ b/.github/support.yml @@ -0,0 +1,29 @@ +# Configuration for support-requests - https://github.com/dessant/support-requests + +# Label used to mark issues as support requests +supportLabel: support + +# Comment to post on issues marked as support requests, `{issue-author}` is an +# optional placeholder. Set to `false` to disable +supportComment: > + :wave: @{issue-author}, we use the issue tracker exclusively for bug reports. + However, this issue appears to be a support request, a feature request, or + attempts to ask a question. + + Please use our support channels to get help with or discuss this project: + + - The [ZoneMinder-Chat Slack channel](https://zoneminder-chat.herokuapp.com/) + + - The [ZoneMinder USer Forum](https://forums.zoneminder.com/) + +# Close issues marked as support requests +close: true + +# Lock issues marked as support requests +lock: true + +# Assign `off-topic` as the reason for locking. Set to `false` to disable +setLockReason: true + +# Repository to extend settings from +# _extends: repo diff --git a/.gitignore b/.gitignore index 0dae662d7..3167e62a6 100644 --- a/.gitignore +++ b/.gitignore @@ -157,3 +157,4 @@ web/undef.log zm.conf zmconfgen.pl zmlinkcontent.sh +**/.DS_Store diff --git a/.travis.yml b/.travis.yml index 1ae0988d5..7cfe96854 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: cpp sudo: required -dist: trusty +dist: xenial git: depth: 9999999 notifications: @@ -32,23 +32,31 @@ install: - update-binfmts --enable qemu-arm env: - global: - - SMPFLAGS=-j4 - matrix: - - OS=el DIST=7 - - OS=fedora DIST=27 DOCKER_REPO=knnniggett/packpack - - OS=fedora DIST=28 DOCKER_REPO=knnniggett/packpack - - OS=ubuntu DIST=trusty - - OS=ubuntu DIST=xenial - - OS=ubuntu DIST=trusty ARCH=i386 - - OS=ubuntu DIST=xenial ARCH=i386 - - OS=raspbian DIST=stretch ARCH=armhf DOCKER_REPO=knnniggett/packpack + - SMPFLAGS=-j4 OS=el DIST=7 + - SMPFLAGS=-j4 OS=el DIST=8 DOCKER_REPO=knnniggett/packpack + - SMPFLAGS=-j4 OS=fedora DIST=30 + - SMPFLAGS=-j4 OS=fedora DIST=31 + - SMPFLAGS=-j4 OS=ubuntu DIST=trusty DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=ubuntu DIST=xenial DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=ubuntu DIST=bionic DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=ubuntu DIST=disco DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=ubuntu DIST=eoan DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=debian DIST=jessie DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=debian DIST=stretch DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=debian DIST=buster DOCKER_REPO=iconzm/packpack + - SMPFLAGS=-j4 OS=ubuntu DIST=trusty ARCH=i386 + - SMPFLAGS=-j4 OS=ubuntu DIST=xenial ARCH=i386 + - SMPFLAGS=-j4 OS=ubuntu DIST=disco ARCH=i386 + - SMPFLAGS=-j4 OS=debian DIST=buster ARCH=i386 + - SMPFLAGS=-j4 OS=debian DIST=stretch ARCH=i386 + - SMPFLAGS=-j4 OS=eslint DIST=eslint compiler: - gcc services: - mysql - docker + script: - utils/packpack/startpackpack.sh @@ -64,4 +72,3 @@ deploy: script: utils/packpack/rsync_xfer.sh on: branch: master - diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index 72d39764f..000000000 --- a/AUTHORS +++ /dev/null @@ -1,10 +0,0 @@ -ZoneMinder - A Linux based camera monitoring and analysis tool. - -This project was imagined and created by Philip Coombes in -September 2002, shortly after being burglarised of his power tools. -He can be contacted at philip.coombes@zoneminder.com - -In early 2013 after nearly two years of no development, -the community reached out to Phil in an attempt to open up the -project. With Phil's blessing, the code was migrated to github, -and the community took over the project. diff --git a/BUGS b/BUGS deleted file mode 100644 index b78c01bed..000000000 --- a/BUGS +++ /dev/null @@ -1 +0,0 @@ -Please see https://github.com/ZoneMinder/ZoneMinder/issues?state=open diff --git a/CMakeLists.txt b/CMakeLists.txt index c847e06a3..b43680b0b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -127,7 +127,8 @@ mark_as_advanced( ZM_PATH_ARP ZM_CONFIG_DIR ZM_CONFIG_SUBDIR - ZM_SYSTEMD) + ZM_SYSTEMD + ZM_MANPAGE_DEST_PREFIX) set(ZM_RUNDIR "/var/run/zm" CACHE PATH "Location of transient process files, default: /var/run/zm") @@ -161,13 +162,12 @@ set(ZM_WEB_GROUP "" CACHE STRING Leave empty to be the same as the web user") set(ZM_DIR_EVENTS "${ZM_CONTENTDIR}/events" CACHE PATH "Location where events are recorded to, default: ZM_CONTENTDIR/events") -set(ZM_DIR_IMAGES "${ZM_CONTENTDIR}/images" CACHE PATH - "Location where images, not directly associated with events, - are recorded to, default: ZM_CONTENTDIR/images") set(ZM_DIR_SOUNDS "sounds" CACHE PATH "Location to look for optional sound files, default: sounds") set(ZM_PATH_ZMS "/cgi-bin/nph-zms" CACHE PATH "Web url to zms streaming server, default: /cgi-bin/nph-zms") +set(ZM_PATH_SHUTDOWN "/sbin/shutdown" CACHE PATH + "Path to shutdown binary, default: /sbin/shutdown") # Advanced set(ZM_PATH_MAP "/dev/shm" CACHE PATH @@ -185,8 +185,6 @@ set(ZM_MYSQL_ENGINE "InnoDB" CACHE STRING set(ZM_NO_MMAP "OFF" CACHE BOOL "Set to ON to not use mmap shared memory. Shouldn't be enabled unless you experience problems with the shared memory. default: OFF") -set(ZM_NO_FFMPEG "OFF" CACHE BOOL - "Set to ON to skip ffmpeg checks and force building ZM without ffmpeg. 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_CURL "OFF" CACHE BOOL @@ -210,6 +208,10 @@ set(ZM_TARGET_DISTRO "" CACHE STRING "Build ZoneMinder for a specific distribution. Currently, valid names are: fc27, fc26, el7, OS13, FreeBSD") set(ZM_SYSTEMD "OFF" CACHE BOOL "Set to ON to force building ZM with systemd support. default: OFF") +set(ZM_MANPAGE_DEST_PREFIX "share/man" CACHE PATH + "Relative path used to install ZoneMinder's Man pages into a + non-standard folder. Most Linux users will not need to change this. + BSD users may need to set this.") # Reassign some variables if a target distro has been specified if((ZM_TARGET_DISTRO MATCHES "^el") OR (ZM_TARGET_DISTRO MATCHES "^fc")) @@ -222,7 +224,6 @@ if((ZM_TARGET_DISTRO MATCHES "^el") OR (ZM_TARGET_DISTRO MATCHES "^fc")) set(ZM_WEBDIR "/usr/share/zoneminder/www") set(ZM_CGIDIR "/usr/libexec/zoneminder/cgi-bin") set(ZM_DIR_EVENTS "/var/lib/zoneminder/events") - set(ZM_DIR_IMAGES "/var/lib/zoneminder/images") set(ZM_PATH_ZMS "/cgi-bin-zm/nph-zms") elseif(ZM_TARGET_DISTRO STREQUAL "OS13") set(ZM_RUNDIR "/var/run/zoneminder") @@ -513,123 +514,137 @@ endif(MP4V2_LIBRARIES) set(PATH_FFMPEG "") set(OPT_FFMPEG "no") -# Do not check for ffmpeg if ZM_NO_FFMPEG is on -if(NOT ZM_NO_FFMPEG) - # avformat (using find_library and find_path) - find_library(AVFORMAT_LIBRARIES avformat) - if(AVFORMAT_LIBRARIES) - set(HAVE_LIBAVFORMAT 1) - list(APPEND ZM_BIN_LIBS "${AVFORMAT_LIBRARIES}") - find_path(AVFORMAT_INCLUDE_DIR "libavformat/avformat.h" /usr/include/ffmpeg) - if(AVFORMAT_INCLUDE_DIR) - include_directories("${AVFORMAT_INCLUDE_DIR}") - set(CMAKE_REQUIRED_INCLUDES "${AVFORMAT_INCLUDE_DIR}") - endif(AVFORMAT_INCLUDE_DIR) - mark_as_advanced(FORCE AVFORMAT_LIBRARIES AVFORMAT_INCLUDE_DIR) - check_include_file("libavformat/avformat.h" HAVE_LIBAVFORMAT_AVFORMAT_H) - set(optlibsfound "${optlibsfound} AVFormat") - else(AVFORMAT_LIBRARIES) - set(optlibsnotfound "${optlibsnotfound} AVFormat") - endif(AVFORMAT_LIBRARIES) +# avformat (using find_library and find_path) +find_library(AVFORMAT_LIBRARIES avformat) +if(AVFORMAT_LIBRARIES) + set(HAVE_LIBAVFORMAT 1) + list(APPEND ZM_BIN_LIBS "${AVFORMAT_LIBRARIES}") + find_path(AVFORMAT_INCLUDE_DIR "libavformat/avformat.h" /usr/include/ffmpeg) + if(AVFORMAT_INCLUDE_DIR) + include_directories("${AVFORMAT_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${AVFORMAT_INCLUDE_DIR}") + endif(AVFORMAT_INCLUDE_DIR) + mark_as_advanced(FORCE AVFORMAT_LIBRARIES AVFORMAT_INCLUDE_DIR) + check_include_file("libavformat/avformat.h" HAVE_LIBAVFORMAT_AVFORMAT_H) + set(optlibsfound "${optlibsfound} AVFormat") +else(AVFORMAT_LIBRARIES) + set(optlibsnotfound "${optlibsnotfound} AVFormat") +endif(AVFORMAT_LIBRARIES) - # avcodec (using find_library and find_path) - find_library(AVCODEC_LIBRARIES avcodec) - if(AVCODEC_LIBRARIES) - set(HAVE_LIBAVCODEC 1) - list(APPEND ZM_BIN_LIBS "${AVCODEC_LIBRARIES}") - find_path(AVCODEC_INCLUDE_DIR "libavcodec/avcodec.h" /usr/include/ffmpeg) - if(AVCODEC_INCLUDE_DIR) - include_directories("${AVCODEC_INCLUDE_DIR}") - set(CMAKE_REQUIRED_INCLUDES "${AVCODEC_INCLUDE_DIR}") - endif(AVCODEC_INCLUDE_DIR) - mark_as_advanced(FORCE AVCODEC_LIBRARIES AVCODEC_INCLUDE_DIR) - check_include_file("libavcodec/avcodec.h" HAVE_LIBAVCODEC_AVCODEC_H) - set(optlibsfound "${optlibsfound} AVCodec") - else(AVCODEC_LIBRARIES) - set(optlibsnotfound "${optlibsnotfound} AVCodec") - endif(AVCODEC_LIBRARIES) +# avcodec (using find_library and find_path) +find_library(AVCODEC_LIBRARIES avcodec) +if(AVCODEC_LIBRARIES) + set(HAVE_LIBAVCODEC 1) + list(APPEND ZM_BIN_LIBS "${AVCODEC_LIBRARIES}") + find_path(AVCODEC_INCLUDE_DIR "libavcodec/avcodec.h" /usr/include/ffmpeg) + if(AVCODEC_INCLUDE_DIR) + include_directories("${AVCODEC_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${AVCODEC_INCLUDE_DIR}") + endif(AVCODEC_INCLUDE_DIR) + mark_as_advanced(FORCE AVCODEC_LIBRARIES AVCODEC_INCLUDE_DIR) + check_include_file("libavcodec/avcodec.h" HAVE_LIBAVCODEC_AVCODEC_H) + set(optlibsfound "${optlibsfound} AVCodec") +else(AVCODEC_LIBRARIES) + set(optlibsnotfound "${optlibsnotfound} AVCodec") +endif(AVCODEC_LIBRARIES) - # avdevice (using find_library and find_path) - find_library(AVDEVICE_LIBRARIES avdevice) - if(AVDEVICE_LIBRARIES) - set(HAVE_LIBAVDEVICE 1) - list(APPEND ZM_BIN_LIBS "${AVDEVICE_LIBRARIES}") - find_path(AVDEVICE_INCLUDE_DIR "libavdevice/avdevice.h" /usr/include/ffmpeg) - if(AVDEVICE_INCLUDE_DIR) - include_directories("${AVDEVICE_INCLUDE_DIR}") - set(CMAKE_REQUIRED_INCLUDES "${AVDEVICE_INCLUDE_DIR}") - endif(AVDEVICE_INCLUDE_DIR) - mark_as_advanced(FORCE AVDEVICE_LIBRARIES AVDEVICE_INCLUDE_DIR) - check_include_file("libavdevice/avdevice.h" HAVE_LIBAVDEVICE_AVDEVICE_H) - set(optlibsfound "${optlibsfound} AVDevice") - else(AVDEVICE_LIBRARIES) - set(optlibsnotfound "${optlibsnotfound} AVDevice") - endif(AVDEVICE_LIBRARIES) +# avdevice (using find_library and find_path) +find_library(AVDEVICE_LIBRARIES avdevice) +if(AVDEVICE_LIBRARIES) + set(HAVE_LIBAVDEVICE 1) + list(APPEND ZM_BIN_LIBS "${AVDEVICE_LIBRARIES}") + find_path(AVDEVICE_INCLUDE_DIR "libavdevice/avdevice.h" /usr/include/ffmpeg) + if(AVDEVICE_INCLUDE_DIR) + include_directories("${AVDEVICE_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${AVDEVICE_INCLUDE_DIR}") + endif(AVDEVICE_INCLUDE_DIR) + mark_as_advanced(FORCE AVDEVICE_LIBRARIES AVDEVICE_INCLUDE_DIR) + check_include_file("libavdevice/avdevice.h" HAVE_LIBAVDEVICE_AVDEVICE_H) + set(optlibsfound "${optlibsfound} AVDevice") +else(AVDEVICE_LIBRARIES) + set(optlibsnotfound "${optlibsnotfound} AVDevice") +endif(AVDEVICE_LIBRARIES) - # avutil (using find_library and find_path) - find_library(AVUTIL_LIBRARIES avutil) - if(AVUTIL_LIBRARIES) - set(HAVE_LIBAVUTIL 1) - list(APPEND ZM_BIN_LIBS "${AVUTIL_LIBRARIES}") - find_path(AVUTIL_INCLUDE_DIR "libavutil/avutil.h" /usr/include/ffmpeg) - if(AVUTIL_INCLUDE_DIR) - include_directories("${AVUTIL_INCLUDE_DIR}") - set(CMAKE_REQUIRED_INCLUDES "${AVUTIL_INCLUDE_DIR}") - endif(AVUTIL_INCLUDE_DIR) - mark_as_advanced(FORCE AVUTIL_LIBRARIES AVUTIL_INCLUDE_DIR) - check_include_file("libavutil/avutil.h" HAVE_LIBAVUTIL_AVUTIL_H) - check_include_file("libavutil/mathematics.h" HAVE_LIBAVUTIL_MATHEMATICS_H) - check_include_file("libavutil/hwcontext.h" HAVE_LIBAVUTIL_HWCONTEXT_H) - set(optlibsfound "${optlibsfound} AVUtil") - else(AVUTIL_LIBRARIES) - set(optlibsnotfound "${optlibsnotfound} AVUtil") - endif(AVUTIL_LIBRARIES) +# avutil (using find_library and find_path) +find_library(AVUTIL_LIBRARIES avutil) +if(AVUTIL_LIBRARIES) + set(HAVE_LIBAVUTIL 1) + list(APPEND ZM_BIN_LIBS "${AVUTIL_LIBRARIES}") + find_path(AVUTIL_INCLUDE_DIR "libavutil/avutil.h" /usr/include/ffmpeg) + if(AVUTIL_INCLUDE_DIR) + include_directories("${AVUTIL_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${AVUTIL_INCLUDE_DIR}") + endif(AVUTIL_INCLUDE_DIR) + mark_as_advanced(FORCE AVUTIL_LIBRARIES AVUTIL_INCLUDE_DIR) + check_include_file("libavutil/avutil.h" HAVE_LIBAVUTIL_AVUTIL_H) + check_include_file("libavutil/mathematics.h" HAVE_LIBAVUTIL_MATHEMATICS_H) + check_include_file("libavutil/hwcontext.h" HAVE_LIBAVUTIL_HWCONTEXT_H) + set(optlibsfound "${optlibsfound} AVUtil") +else(AVUTIL_LIBRARIES) + set(optlibsnotfound "${optlibsnotfound} AVUtil") +endif(AVUTIL_LIBRARIES) - # swscale (using find_library and find_path) - find_library(SWSCALE_LIBRARIES swscale) - if(SWSCALE_LIBRARIES) - set(HAVE_LIBSWSCALE 1) - list(APPEND ZM_BIN_LIBS "${SWSCALE_LIBRARIES}") - find_path(SWSCALE_INCLUDE_DIR "libswscale/swscale.h" /usr/include/ffmpeg) - if(SWSCALE_INCLUDE_DIR) - include_directories("${SWSCALE_INCLUDE_DIR}") - set(CMAKE_REQUIRED_INCLUDES "${SWSCALE_INCLUDE_DIR}") - endif(SWSCALE_INCLUDE_DIR) - mark_as_advanced(FORCE SWSCALE_LIBRARIES SWSCALE_INCLUDE_DIR) - check_include_file("libswscale/swscale.h" HAVE_LIBSWSCALE_SWSCALE_H) - set(optlibsfound "${optlibsfound} SWScale") - else(SWSCALE_LIBRARIES) - set(optlibsnotfound "${optlibsnotfound} SWScale") - endif(SWSCALE_LIBRARIES) +# swscale (using find_library and find_path) +find_library(SWSCALE_LIBRARIES swscale) +if(SWSCALE_LIBRARIES) + set(HAVE_LIBSWSCALE 1) + list(APPEND ZM_BIN_LIBS "${SWSCALE_LIBRARIES}") + find_path(SWSCALE_INCLUDE_DIR "libswscale/swscale.h" /usr/include/ffmpeg) + if(SWSCALE_INCLUDE_DIR) + include_directories("${SWSCALE_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${SWSCALE_INCLUDE_DIR}") + endif(SWSCALE_INCLUDE_DIR) + mark_as_advanced(FORCE SWSCALE_LIBRARIES SWSCALE_INCLUDE_DIR) + check_include_file("libswscale/swscale.h" HAVE_LIBSWSCALE_SWSCALE_H) + set(optlibsfound "${optlibsfound} SWScale") +else(SWSCALE_LIBRARIES) + set(optlibsnotfound "${optlibsnotfound} SWScale") +endif(SWSCALE_LIBRARIES) - # rescale (using find_library and find_path) - find_library(AVRESAMPLE_LIBRARIES avresample) - if(AVRESAMPLE_LIBRARIES) - set(HAVE_LIBAVRESAMPLE 1) - list(APPEND ZM_BIN_LIBS "${AVRESAMPLE_LIBRARIES}") - find_path(AVRESAMPLE_INCLUDE_DIR "libavresample/avresample.h" /usr/include/ffmpeg) - if(AVRESAMPLE_INCLUDE_DIR) - include_directories("${AVRESAMPLE_INCLUDE_DIR}") - set(CMAKE_REQUIRED_INCLUDES "${AVRESAMPLE_INCLUDE_DIR}") - endif(AVRESAMPLE_INCLUDE_DIR) - mark_as_advanced(FORCE AVRESAMPLE_LIBRARIES AVRESAMPLE_INCLUDE_DIR) - check_include_file("libavresample/avresample.h" HAVE_LIBAVRESAMPLE_AVRESAMPLE_H) - set(optlibsfound "${optlibsfound} AVResample") - else(AVRESAMPLE_LIBRARIES) - set(optlibsnotfound "${optlibsnotfound} AVResample") - endif(AVRESAMPLE_LIBRARIES) +# SWresample (using find_library and find_path) +find_library(SWRESAMPLE_LIBRARIES swresample) +if(SWRESAMPLE_LIBRARIES) + set(HAVE_LIBSWRESAMPLE 1) + list(APPEND ZM_BIN_LIBS "${SWRESAMPLE_LIBRARIES}") + find_path(SWRESAMPLE_INCLUDE_DIR "libswresample/swresample.h" /usr/include/ffmpeg) + if(SWRESAMPLE_INCLUDE_DIR) + include_directories("${SWRESAMPLE_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${SWRESAMPLE_INCLUDE_DIR}") + endif(SWRESAMPLE_INCLUDE_DIR) + mark_as_advanced(FORCE SWRESAMPLE_LIBRARIES SWRESAMPLE_INCLUDE_DIR) + check_include_file("libswresample/swresample.h" HAVE_LIBSWRESAMPLE_SWRESAMPLE_H) + set(optlibsfound "${optlibsfound} SWResample") +else(SWRESAMPLE_LIBRARIES) + set(optlibsnotfound "${optlibsnotfound} SWResample") - # Find the path to the ffmpeg executable - find_program(FFMPEG_EXECUTABLE - NAMES ffmpeg avconv - PATH_SUFFIXES ffmpeg) - if(FFMPEG_EXECUTABLE) - set(PATH_FFMPEG "${FFMPEG_EXECUTABLE}") - set(OPT_FFMPEG "yes") - mark_as_advanced(FFMPEG_EXECUTABLE) - endif(FFMPEG_EXECUTABLE) + # AVresample (using find_library and find_path) + find_library(AVRESAMPLE_LIBRARIES avresample) + if(AVRESAMPLE_LIBRARIES) + set(HAVE_LIBAVRESAMPLE 1) + list(APPEND ZM_BIN_LIBS "${AVRESAMPLE_LIBRARIES}") + find_path(AVRESAMPLE_INCLUDE_DIR "libavresample/avresample.h" /usr/include/ffmpeg) + if(AVRESAMPLE_INCLUDE_DIR) + include_directories("${AVRESAMPLE_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${AVRESAMPLE_INCLUDE_DIR}") + endif(AVRESAMPLE_INCLUDE_DIR) + mark_as_advanced(FORCE AVRESAMPLE_LIBRARIES AVRESAMPLE_INCLUDE_DIR) + check_include_file("libavresample/avresample.h" HAVE_LIBAVRESAMPLE_AVRESAMPLE_H) + set(optlibsfound "${optlibsfound} AVResample") + else(AVRESAMPLE_LIBRARIES) + set(optlibsnotfound "${optlibsnotfound} AVResample") + endif(AVRESAMPLE_LIBRARIES) -endif(NOT ZM_NO_FFMPEG) +endif(SWRESAMPLE_LIBRARIES) + +# Find the path to the ffmpeg executable +find_program(FFMPEG_EXECUTABLE + NAMES ffmpeg avconv + PATH_SUFFIXES ffmpeg) +if(FFMPEG_EXECUTABLE) + set(PATH_FFMPEG "${FFMPEG_EXECUTABLE}") + set(OPT_FFMPEG "yes") + mark_as_advanced(FFMPEG_EXECUTABLE) +endif(FFMPEG_EXECUTABLE) # Do not check for libvlc if ZM_NO_LIBVLC is on if(NOT ZM_NO_LIBVLC) @@ -802,6 +817,24 @@ if(WITH_SYSTEMD) endif(NOT POLKIT_FOUND) endif(WITH_SYSTEMD) +# Find the path to an arp compatible executable +if(ZM_PATH_ARP STREQUAL "") + find_program(ARP_EXECUTABLE arp) + if(ARP_EXECUTABLE) + set(ZM_PATH_ARP "${ARP_EXECUTABLE}") + mark_as_advanced(ARP_EXECUTABLE) + else(ARP_EXECUTABLE) + find_program(ARP_EXECUTABLE ip) + if(ARP_EXECUTABLE) + set(ZM_PATH_ARP "${ARP_EXECUTABLE} neigh") + mark_as_advanced(ARP_EXECUTABLE) + endif(ARP_EXECUTABLE) + endif(ARP_EXECUTABLE) + if(ARP_EXECUTABLE-NOTFOUND) + message(WARNING "Unable to find a compatible arp binary. Monitor probe will not function." ) + endif(ARP_EXECUTABLE-NOTFOUND) +endif(ZM_PATH_ARP STREQUAL "") + # Some variables that zm expects set(ZM_PID "${ZM_RUNDIR}/zm.pid") set(ZM_CONFIG "${ZM_CONFIG_DIR}/zm.conf") @@ -839,6 +872,13 @@ include(Pod2Man) ADD_MANPAGE_TARGET() # Process subdirectories + +# build a bcrypt static library +set(BUILD_SHARED_LIBS_SAVED "${BUILD_SHARED_LIBS}") +set(BUILD_SHARED_LIBS OFF) +add_subdirectory(src/libbcrypt EXCLUDE_FROM_ALL) +set(BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS_SAVED}") + add_subdirectory(src) add_subdirectory(scripts) add_subdirectory(db) diff --git a/ChangeLog b/ChangeLog deleted file mode 100644 index afab3413a..000000000 --- a/ChangeLog +++ /dev/null @@ -1 +0,0 @@ -This is too hard to maintain. See https://github.com/ZoneMinder/ZoneMinder/commits/master diff --git a/INSTALL b/INSTALL deleted file mode 100644 index 666105c40..000000000 --- a/INSTALL +++ /dev/null @@ -1,149 +0,0 @@ -Installing ZoneMinder with cmake --------------------------------- -Starting with ZoneMinder 1.26.4, ZoneMinder can now be installed using cmake. This requires cmake version 2.6 or newer. -cmake is an alternative to the autotools collection (libtool, autoconf, automake, autoheader and such). Its more recent and has many advantages, including, but not limited to: -* One program (cmake) instead of multiple. (libtool, autoconf, automake, etc) -* One file per directory (CMakeLists.txt) instead of multiple. (configure.ac, Makefile.am and sometimes more) -* One syntax (cmake's syntax) instead of multiple. (bash and m4) -* Generation of makefiles for many platforms, including Windows. -* Newer than autotools and is being actively developed. -* Generates colored makefiles with progress indicator. -* Slightly faster because its based on C and not bash. -* Lots of documentation, unlike autotools: http://www.cmake.org/cmake/help/cmake2.6docs.html - -At this point, its still possible to use autotools for the ZoneMinder project. Choosing cmake or autotools is now a matter of preference. -Hopefully in the future, cmake will become the default way to install ZoneMinder. - -Important differences ---------------------- -* Unlike the autotools way, the cmake way does not require any options. It attempts to detect some things by its own (system directories, libarch, web user and group) and uses defaults for others (installation paths and such). -* Unlike the autotools way, which links the binaries to a fixed list of libraries, the cmake way only links to libraries that it found on the system. If a library is not found, but required, a fatal error will be shown during the configuration step. -* Unlike the autotools way, the cmake way does not modify the system in any way it shouldn't. It only does what its supposed to do: Install files to your system. Nothing else and nothing leaks out of the DESTDIR environment variable (if used). This means that depending on your configuration, there might be an extra required step after installation: to link WEB_PATH/events and WEB_PATH/images folders to the correct places. - -Configuration -------------- -cmake by default does not require any parameters, but its possible to override the defaults with the options below. - -Configuration can be done in 4 ways: -1) As a command line parameter, e.g. cmake -DCMAKE_VERBOSE_MAKEFILE=ON . -2) Using cmake-gui -3) Providing cmake with an initial cache file with the -C option - IMPORTANT: Do not use the -C option if any major part of your system, excluding the version of zoneminder, has changed. - For example, do not use this option if: you have upgraded your distro to a new version, have gone from 32 to 64 bits, - or have migrated from one machine to another. -4) By editing the cache file CMakeCache.txt (after it has been generated) - Not recommended - -Possible configuration options: - ZM_RUNDIR Location of transient process files, default: /var/run/zm - ZM_SOCKDIR Location of Unix domain socket files, default /var/run/zm - ZM_TMPDIR Location of temporary files, default: /tmp/zm - ZM_LOGDIR Location of generated log files, default: /var/log/zm - ZM_WEBDIR Location of the web files, default: /share/zoneminder/www - ZM_CGIDIR Location of the cgi-bin files, default: /libexec/zoneminder/cgi-bin - ZM_CONTENTDIR Location of dynamic content (events and images), default: /var/lib/zoneminder - ZM_DB_HOST Hostname where ZoneMinder database located, default: localhost - ZM_DB_NAME Name of ZoneMinder database, default: zm - ZM_DB_USER Name of ZoneMinder database user, default: zmuser - ZM_DB_PASS Password of ZoneMinder database user, default: zmpass - ZM_DB_SSL_CA_CERT Path to SSL CA certificate, default: empty; SSL not enabled - ZM_DB_SSL_CLIENT_KEY Path to SSL client key, default: empty; SSL not enabled - ZM_DB_SSL_CLIENT_CERT Path to SSL client certificate, default: empty; SSL not enabled - ZM_WEB_USER The user apache or the local web server runs on. Leave empty for automatic detection. If that fails, you can use this variable to force - ZM_WEB_GROUP The group apache or the local web server runs on, Leave empty to be the same as the web user - ZM_DIR_EVENTS Location where events are recorded to, default: ZM_CONTENTDIR/events - ZM_DIR_IMAGES Location where images, not directly associated with events, are recorded to, default: ZM_CONTENTDIR/images - ZM_DIR_SOUNDS Location to look for optional sound files, default: sounds - ZM_PATH_ZMS Web url to zms streaming server, default: /cgi-bin/nph-zms -Advanced: - ZM_PATH_MAP Location to save mapped memory files, default: /dev/shm - ZM_PATH_ARP Full path to compatible arp binary. Leave empty for automatic detection. - ZM_CONFIG_DIR Location of the main ZoneMinder config file, zm.conf. default: /etc/zm - ZM_CONFIG_SUBDIR Location of custom config files. default: ZM_CONFIG_DIR/conf.d - ZM_EXTRA_LIBS A list of optional libraries, separated by semicolons, e.g. ssl;theora - ZM_MYSQL_ENGINE MySQL engine to use with database, default: InnoDB - ZM_NO_MMAP Set to ON to not use mmap shared memory. Shouldn't be enabled unless you experience problems with the shared memory. default: OFF - ZM_NO_FFMPEG Set to ON to skip ffmpeg checks and force building ZM without ffmpeg. default: OFF - ZM_NO_X10 Set to ON to build ZoneMinder without X10 support. default: OFF - ZM_PERL_MM_PARMS By default, ZoneMinder's Perl modules are installed into the Vendor folders, as defined by your installation of Perl. You can change that here. Consult Perl's MakeMaker documentation for a definition of acceptable parameters. If you set this to something that causes the modules to be installed outside Perl's normal serach path, then you will also need to set ZM_PERL_SEARCH_PATH accordingly. default: "INSTALLDIRS=vendor NO_PACKLIST=1 NO_PERLLOCAL=1" - ZM_PERL_SEARCH_PATH Use to add a folder to your Perl's search path. This will need to be set in cases where ZM_PERL_MM_PARMS has been modified such that ZoneMinder's Perl modules are installed outside Perl's default search path. default: "" - -Useful configuration options provided by cmake: -CMAKE_VERBOSE_MAKEFILE - Set this to ON (default OFF) to see what cmake is doing. Very useful for troubleshooting. -CMAKE_BUILD_TYPE - Set this to Debug (default Release) to build ZoneMinder with debugging enabled. -CMAKE_INSTALL_PREFIX - Use this to change the prefix (default /usr/local). This option behaves like --prefix from autoconf. Package maintainers will probably want to set this to "/usr". - -Useful environment variables provided by cmake: -CMAKE_INCLUDE_PATH - Use this to add to the include search path. -CMAKE_LIBRARY_PATH - Use this to add to the library search path. -CMAKE_PREFIX_PATH - Use this to add to both include and library search paths. /include will be added to the include search path and /lib to the library search path. Multiple paths can be specified, separated by a : character. For example: export CMAKE_PREFIX_PATH="/opt/libjpeg-turbo:/opt/ffmpeg-from-git" - -CFLAGS, CPPFLAGS and other environment variables: -To append to the CFLAGS and CXXFLAGS, please use the CFLAGS and CXXFLAGS environment variables. -Or use the CMAKE_C_FLAGS and CMAKE_CXX_FLAGS configuration options. -To replace the CFLAGS and CXXFLAGS entirely: -* For the Release build type: use CMAKE_C_FLAGS_RELEASE for the CFLAGS and CMAKE_CXX_FLAGS_RELEASE for the CXXFLAGS -* For the Debug build type: use CMAKE_C_FLAGS_DEBUG for the CFLAGS and CMAKE_CXX_FLAGS_DEBUG for the CXXFLAGS -Other important environment variables (such as LDFLAGS) are also supported. - -The DESTDIR environment variable is also supported, however it needs to be set before invoking make install. For example: DESTDIR=mydestdir make install -For more information about DESTDIR, see: -* http://www.gnu.org/prep/standards/html_node/DESTDIR.html - -Basic steps for installing ZoneMinder on a fresh system -------------------------------------------------------- -1) After installing all the required dependencies, in the project directory, run "cmake [extra options] ." -This behaves like ./configure. It is also possible to supply configuration options, e.g. cmake -DZM_DB_PASS="mypass" . -IMPORTANT: Don't forget the dot "." at the end. -2) Run "make" to compile ZoneMinder -3) Run "make install" (as root, or use sudo) to install ZoneMinder to your system. -4) Create a directory for the content and the necessary symlinks by running zmlinkcontent.sh with the directory you want to use. e.g. ./zmlinkcontent.sh /nfs/zm -5) Create a database for zoneminder, called "zm". -6) Create a user for the zoneminder database, called zmuser with password and full privileges to the "zm" database. -NOTE: The database server, database name, user and password can be different and adjusted during configuration step with the options in this file, or by editing /etc/zm.conf -7) Populate the zoneminder database using the script zm_create.sql. This should be found in /share/zoneminder/db or in the project/db directory. - -8) Create an apache virtual host for ZoneMinder. Make sure to use the same paths as ZM_WEBDIR and ZM_CGIDIR in /etc/zm.conf -9) Verify date.timezone is set to your timezone. This parameter is often found inside the system php.ini file. Consult your distribution's documentation for the proper way to set this value. -10) Create other config if desired (e.g. rsyslog, logrotate and such). Some of this can be found in /share/zoneminder/misc or project/misc directory -11) Setup an appropriate startup script for your system. Two generic startup scripts have been provided, a legacy Sys V Init script and a Systemd service file. - -*Sys V Init Setup* -- Copy the sys v init script /scripts/zm from the build folder to /etc/init. -- Inspect the contents to make sure they apply to your distro. - -*SystemD Setup* -- Copy the zoneminder systemd service file /misc/zoneminder.service from the build folder to the systemd service file location. - For Redhat based distros, that folder is /usr/lib/systemd/system. -- Inspect the contents to make sure they apply to your distro. -- Tell systemd to load the new service file: "sudo systemctl daemon-reload". -- Copy /misc/zoneminder-tmpfiles.conf to /etc/tmpfiles.d -- Tell systemd to process this file: "sudo /usr/bin/systemd-tmpfiles --create /etc/tmpfiles.d/zoneminder.conf". - -Basic steps for upgrading ZoneMinder ------------------------------------- -1) If you wish to use the same paths and configuration as the currently installed ZoneMinder, you need to provide cmake with options that match your current installation. -You can provide those options in the command line to cmake, e.g. cmake -DZM_DB_PASS="blah" -DZM_WEBDIR="/usr/local/share/zoneminder/www" -DCMAKE_INSTALL_FULL_BINDIR="/usr/bin" . -Or alternatively, for convenience, use the cmakecacheimport.sh script. This reads a zoneminder configuration file (zm.conf) and creates a cmake initial cache file called zm_conf.cmake, which you can then provide to cmake. -For example: -./cmakecacheimport.sh /etc/zm.conf -cmake -C zm_conf.cmake [extra options] . - -2) Run "make" to compile ZoneMinder -3) Run "make install" (as root, or use sudo) to install ZoneMinder to your system. -4) Depending on your configuration: If the DIR_EVENTS and DIR_IMAGES options are set to default (pointing to web directory/events and web directory/images), You will need to update the symlinks in the web directory to the correct folders. e.g. web directory/events should point to the real events directory, and likewise for the images directory. -You can use the zmlinkcontent.sh script for this. For example, if /var/lib/zoneminder is the folder that contains the "images" and "events" directories, you can use: -./zmlinkcontent.sh /var/lib/zoneminder -By default, the content directory for new installations is /var/lib/zoneminder. This can be overridden in cmake with the ZM_CONTENTDIR option. e.g. cmake -DZM_CONTENTDIR="/some/big/storage/zm" . - -5) Run zmupdate.pl to update the database layout to the new version. - -Uninstallation: ---------------- -By default, cmake does not have an uninstall target, however we have added a one. Simply run make uninstall (or DESTDIR=mydestdir make uninstall if a DESTDIR was used) and it will remove all the files that cmake installed. -It's also possible to do this manually. The file install_manifest.txt contains the list of files installed to the system. This can be used in many ways to delete all files installed by cmake, such as: xargs rm < install_manifest.txt - -Contributions: --------------- -Please visit our GitHub at http://github.com/ZoneMinder/ZoneMinder - - diff --git a/NEWS b/NEWS deleted file mode 100644 index 3160e2dce..000000000 --- a/NEWS +++ /dev/null @@ -1 +0,0 @@ -Please see README file. diff --git a/README.md b/README.md index 3bdf1ffd1..b9d215a40 100644 --- a/README.md +++ b/README.md @@ -1,7 +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) +[![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 @@ -29,7 +32,7 @@ Better methods exist today that do much of this for you. The current development 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) @@ -40,7 +43,7 @@ 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 a ZoneMinder Package +### 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. @@ -51,9 +54,9 @@ Lastly, if you desire to build a development snapshot from the master branch, it Please visit our [ReadtheDocs site](https://zoneminder.readthedocs.org/en/stable/installationguide/index.html) for distro specific instructions. ### Package Maintainers -Many of the ZoneMinder configration variable default values are not configurable at build time through autotools or cmake. A new tool called *zmeditconfigdata.sh* has been added to allow package maintainers to manipulate any variable stored in ConfigData.pm without patching the source. +Many of the ZoneMinder configuration variable default values are not configurable at build time through autotools or cmake. A new tool called *zmeditconfigdata.sh* has been added to allow package maintainers to manipulate any variable stored in ConfigData.pm without patching the source. -For example, let's say I have created a new ZoneMinder package that contains the cambolzola javascript file. However, by default cambozola support is turned off. To fix that, add this to the pacakging script: +For example, let's say I have created a new ZoneMinder package that contains the cambozola javascript file. However, by default cambozola support is turned off. To fix that, add this to the packaging script: ```bash ./utils/zmeditconfigdata.sh ZM_OPT_CAMBOZOLA yes ``` diff --git a/TODO b/TODO deleted file mode 100644 index f449b2a7e..000000000 --- a/TODO +++ /dev/null @@ -1,2 +0,0 @@ -Please see README.md file. - diff --git a/cmake/Modules/Pod2Man.cmake b/cmake/Modules/Pod2Man.cmake index 6804fab53..734be239b 100644 --- a/cmake/Modules/Pod2Man.cmake +++ b/cmake/Modules/Pod2Man.cmake @@ -21,7 +21,7 @@ # To use it, include this file in CMakeLists.txt and # invoke POD2MAN(
) -MACRO(POD2MAN PODFILE MANFILE SECTION) +MACRO(POD2MAN PODFILE MANFILE SECTION MANPAGE_DEST_PREFIX) FIND_PROGRAM(POD2MAN pod2man) FIND_PROGRAM(GZIP gzip) @@ -58,9 +58,9 @@ MACRO(POD2MAN PODFILE MANFILE SECTION) INSTALL( FILES ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}.gz - DESTINATION share/man/man${SECTION} + DESTINATION ${MANPAGE_DEST_PREFIX}/man${SECTION} ) -ENDMACRO(POD2MAN PODFILE MANFILE SECTION) +ENDMACRO(POD2MAN PODFILE MANFILE SECTION MANPAGE_DEST_PREFIX) MACRO(ADD_MANPAGE_TARGET) # It is not possible add a dependency to target 'install' diff --git a/conf.d/01-system-paths.conf.in b/conf.d/01-system-paths.conf.in index 9f45abdbd..e1aaf0bef 100644 --- a/conf.d/01-system-paths.conf.in +++ b/conf.d/01-system-paths.conf.in @@ -16,11 +16,6 @@ # The web account user must have full read/write permission to this folder. ZM_DIR_EVENTS=@ZM_DIR_EVENTS@ -# Full path to the folder images, not directly associated with events, -# are recorded to. -# The web account user must have full read/write permission to this folder. -ZM_DIR_IMAGES=@ZM_DIR_IMAGES@ - # Foldername under the webroot where ZoneMinder looks for optional sound files # to play when an alarm is detected. ZM_DIR_SOUNDS=@ZM_DIR_SOUNDS@ @@ -50,4 +45,4 @@ 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@ +ZM_PATH_ARP="@ZM_PATH_ARP@" diff --git a/db/triggers.sql b/db/triggers.sql index 2f13be435..87c8465b4 100644 --- a/db/triggers.sql +++ b/db/triggers.sql @@ -4,8 +4,8 @@ DROP TRIGGER IF EXISTS Events_Hour_delete_trigger// CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour FOR EACH ROW BEGIN UPDATE Monitors SET - HourEvents = COALESCE(HourEvents,1)-1, - HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) + HourEvents = GREATEST(COALESCE(HourEvents,1)-1,0), + HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Id=OLD.MonitorId; END; // @@ -20,23 +20,21 @@ FOR EACH ROW set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); IF ( diff ) THEN IF ( NEW.MonitorID != OLD.MonitorID ) THEN - UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId; - UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; + UPDATE Monitors SET HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId; + UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; ELSE UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId; END IF; END IF; END; // -DELIMITER ; -delimiter // DROP TRIGGER IF EXISTS Events_Day_delete_trigger// CREATE TRIGGER Events_Day_delete_trigger BEFORE DELETE ON Events_Day FOR EACH ROW BEGIN UPDATE Monitors SET - DayEvents = COALESCE(DayEvents,1)-1, - DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) + DayEvents = GREATEST(COALESCE(DayEvents,1)-1,0), + DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Id=OLD.MonitorId; END; // @@ -50,10 +48,10 @@ FOR EACH ROW set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); IF ( diff ) THEN IF ( NEW.MonitorID != OLD.MonitorID ) THEN - UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId; + UPDATE Monitors SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId; UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; ELSE - UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId; + UPDATE Monitors SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId; END IF; END IF; END; @@ -64,8 +62,8 @@ DROP TRIGGER IF EXISTS Events_Week_delete_trigger// CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week FOR EACH ROW BEGIN UPDATE Monitors SET - WeekEvents = COALESCE(WeekEvents,1)-1, - WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) + WeekEvents = GREATEST(COALESCE(WeekEvents,1)-1,0), + WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Id=OLD.MonitorId; END; // @@ -79,10 +77,10 @@ FOR EACH ROW set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); IF ( diff ) THEN IF ( NEW.MonitorID != OLD.MonitorID ) THEN - UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId; + UPDATE Monitors SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId; UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; ELSE - UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId; + UPDATE Monitors SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId; END IF; END IF; END; @@ -92,8 +90,8 @@ DROP TRIGGER IF EXISTS Events_Month_delete_trigger// CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month FOR EACH ROW BEGIN UPDATE Monitors SET - MonthEvents = COALESCE(MonthEvents,1)-1, - MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) + MonthEvents = GREATEST(COALESCE(MonthEvents,1)-1,0), + MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Id=OLD.MonitorId; END; // @@ -107,10 +105,10 @@ FOR EACH ROW set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); IF ( diff ) THEN IF ( NEW.MonitorID != OLD.MonitorID ) THEN - UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace) WHERE Monitors.Id=OLD.MonitorId; + UPDATE Monitors SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace),0) WHERE Monitors.Id=OLD.MonitorId; UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+COALESCE(NEW.DiskSpace) WHERE Monitors.Id=NEW.MonitorId; ELSE - UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId; + UPDATE Monitors SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId; END IF; END IF; END; @@ -128,14 +126,14 @@ BEGIN set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); IF ( NEW.StorageId = OLD.StorageID ) THEN IF ( diff ) THEN - UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + diff WHERE Id = OLD.StorageId; + UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) + diff,0) WHERE Id = OLD.StorageId; END IF; ELSE IF ( NEW.DiskSpace ) THEN UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + NEW.DiskSpace WHERE Id = NEW.StorageId; END IF; IF ( OLD.DiskSpace ) THEN - UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) - OLD.DiskSpace WHERE Id = OLD.StorageId; + UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - OLD.DiskSpace,0) WHERE Id = OLD.StorageId; END IF; END IF; @@ -150,12 +148,16 @@ BEGIN UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=NEW.MonitorId; ELSEIF ( OLD.Archived ) THEN DELETE FROM Events_Archived WHERE EventId=OLD.Id; - UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)-1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) WHERE Id=OLD.MonitorId; + UPDATE Monitors + SET + ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,0)-1,0), + ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; ELSE IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; UPDATE Monitors SET - ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0) + ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0) WHERE Id=OLD.MonitorId; END IF; END IF; @@ -164,18 +166,18 @@ BEGIN END IF; IF ( diff ) THEN - UPDATE Monitors SET TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=OLD.MonitorId; + UPDATE Monitors + SET + TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; END IF; END; // -delimiter ; +DROP TRIGGER IF EXISTS event_insert_trigger// -DROP TRIGGER IF EXISTS event_insert_trigger; - -delimiter // /* The assumption is that when an Event is inserted, it has no size yet, so don't bother updating the DiskSpace, just the count. * The DiskSpace will get update in the Event Update Trigger */ @@ -203,7 +205,7 @@ CREATE TRIGGER event_delete_trigger BEFORE DELETE ON Events FOR EACH ROW BEGIN IF ( OLD.DiskSpace ) THEN - UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) - CAST(OLD.DiskSpace AS SIGNED) WHERE Id = OLD.StorageId; + UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) WHERE Id = OLD.StorageId; END IF; DELETE FROM Events_Hour WHERE EventId=OLD.Id; DELETE FROM Events_Day WHERE EventId=OLD.Id; @@ -212,15 +214,15 @@ BEGIN IF ( OLD.Archived ) THEN DELETE FROM Events_Archived WHERE EventId=OLD.Id; UPDATE Monitors SET - ArchivedEvents = COALESCE(ArchivedEvents,1) - 1, - ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0), - TotalEvents = COALESCE(TotalEvents,1) - 1, - TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,1) - 1,0), + ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0), + TotalEvents = GREATEST(COALESCE(TotalEvents,1) - 1,0), + TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) WHERE Id=OLD.MonitorId; ELSE UPDATE Monitors SET - TotalEvents = COALESCE(TotalEvents,1)-1, - TotalEventDiskSpace=COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) + TotalEvents = GREATEST(COALESCE(TotalEvents,1)-1,0), + TotalEventDiskSpace=GREATEST(COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Id=OLD.MonitorId; END IF; END; diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 83b75f768..6b561a116 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -63,11 +63,12 @@ DROP TABLE IF EXISTS `Controls`; CREATE TABLE `Controls` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', - `Type` enum('Local','Remote','Ffmpeg','Libvlc','cURL','WebSite') NOT NULL default 'Local', + `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') 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', `CanReset` tinyint(3) unsigned NOT NULL default '0', + `CanReboot` tinyint(3) unsigned NOT NULL default '0', `CanZoom` tinyint(3) unsigned NOT NULL default '0', `CanAutoZoom` tinyint(3) unsigned NOT NULL default '0', `CanZoomAbs` tinyint(3) unsigned NOT NULL default '0', @@ -185,6 +186,7 @@ CREATE TABLE `Events` ( `Id` bigint unsigned NOT NULL auto_increment, `MonitorId` int(10) unsigned NOT NULL default '0', `StorageId` smallint(5) unsigned default 0, + `SecondaryStorageId` smallint(5) unsigned default 0, `Name` varchar(64) NOT NULL default '', `Cause` varchar(32) NOT NULL default '', `StartTime` datetime default NULL, @@ -207,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, @@ -223,7 +225,7 @@ CREATE TABLE `Events_Hour` ( `EventId` BIGINT unsigned NOT NULL, `MonitorId` int(10) unsigned NOT NULL, `StartTime` datetime default NULL, - `DiskSpace` bigint unsigned default NULL, + `DiskSpace` bigint default NULL, PRIMARY KEY (`EventId`), KEY `Events_Hour_MonitorId_idx` (`MonitorId`), KEY `Events_Hour_StartTime_idx` (`StartTime`) @@ -234,7 +236,7 @@ CREATE TABLE `Events_Day` ( `EventId` BIGINT unsigned NOT NULL, `MonitorId` int(10) unsigned NOT NULL, `StartTime` datetime default NULL, - `DiskSpace` bigint unsigned default NULL, + `DiskSpace` bigint default NULL, PRIMARY KEY (`EventId`), KEY `Events_Day_MonitorId_idx` (`MonitorId`), KEY `Events_Day_StartTime_idx` (`StartTime`) @@ -245,7 +247,7 @@ CREATE TABLE `Events_Week` ( `EventId` BIGINT unsigned NOT NULL, `MonitorId` int(10) unsigned NOT NULL, `StartTime` datetime default NULL, - `DiskSpace` bigint unsigned default NULL, + `DiskSpace` bigint default NULL, PRIMARY KEY (`EventId`), KEY `Events_Week_MonitorId_idx` (`MonitorId`), KEY `Events_Week_StartTime_idx` (`StartTime`) @@ -256,7 +258,7 @@ CREATE TABLE `Events_Month` ( `EventId` BIGINT unsigned NOT NULL, `MonitorId` int(10) unsigned NOT NULL, `StartTime` datetime default NULL, - `DiskSpace` bigint unsigned default NULL, + `DiskSpace` bigint default NULL, PRIMARY KEY (`EventId`), KEY `Events_Month_MonitorId_idx` (`MonitorId`), KEY `Events_Month_StartTime_idx` (`StartTime`) @@ -267,7 +269,7 @@ DROP TABLE IF EXISTS `Events_Archived`; CREATE TABLE `Events_Archived` ( `EventId` BIGINT unsigned NOT NULL, `MonitorId` int(10) unsigned NOT NULL, - `DiskSpace` bigint unsigned default NULL, + `DiskSpace` bigint default NULL, PRIMARY KEY (`EventId`), KEY `Events_Archived_MonitorId_idx` (`MonitorId`) ) ENGINE=@ZM_MYSQL_ENGINE@; @@ -280,7 +282,7 @@ 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, + `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', @@ -291,6 +293,8 @@ CREATE TABLE `Filters` ( `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', @@ -350,6 +354,7 @@ CREATE INDEX `Groups_Monitors_MonitorId_idx` ON `Groups_Monitors` (`MonitorId`); DROP TABLE IF EXISTS `Logs`; CREATE TABLE `Logs` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT, `TimeKey` decimal(16,6) NOT NULL, `Component` varchar(32) NOT NULL, `ServerId` int(10) unsigned, @@ -359,6 +364,7 @@ CREATE TABLE `Logs` ( `Message` text NOT NULL, `File` varchar(255) DEFAULT NULL, `Line` smallint(5) unsigned DEFAULT NULL, + PRIMARY KEY (`Id`), KEY `TimeKey` (`TimeKey`) ) ENGINE=@ZM_MYSQL_ENGINE@; @@ -397,7 +403,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','NVSocket') NOT NULL default 'Local', + `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL default 'Local', `Device` tinytext, `Channel` tinyint(3) unsigned default NULL, `Format` int(10) unsigned default NULL, @@ -410,7 +416,7 @@ CREATE TABLE `MonitorPresets` ( `Width` smallint(5) unsigned default NULL, `Height` smallint(5) unsigned default NULL, `Palette` int(10) unsigned default NULL, - `MaxFPS` decimal(5,2) default NULL, + `MaxFPS` decimal(5,3) default NULL, `Controllable` tinyint(3) unsigned NOT NULL default '0', `ControlId` varchar(16) default NULL, `ControlDevice` varchar(255) default NULL, @@ -428,9 +434,10 @@ 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') NOT NULL default 'Local', + `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') 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), @@ -453,8 +460,10 @@ 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), `SaveJPEGs` TINYINT NOT NULL DEFAULT '3' , `VideoWriter` TINYINT NOT NULL DEFAULT '0', `OutputCodec` enum('h264','mjpeg','mpeg1','mpeg2'), @@ -478,6 +487,7 @@ CREATE TABLE `Monitors` ( `StreamReplayBuffer` int(10) unsigned NOT NULL default '1000', `AlarmFrameCount` smallint(5) unsigned NOT NULL default '1', `SectionLength` int(10) unsigned NOT NULL default '600', + `MinSectionLength` int(10) unsigned NOT NULL default '10', `FrameSkip` smallint(5) unsigned NOT NULL default '0', `MotionFrameSkip` smallint(5) unsigned NOT NULL default '0', `AnalysisFPSLimit` decimal(5,2) default NULL, @@ -496,9 +506,9 @@ CREATE TABLE `Monitors` ( `TrackDelay` smallint(5) unsigned, `ReturnLocation` tinyint(3) NOT NULL default '-1', `ReturnDelay` smallint(5) unsigned, - `DefaultView` enum('Events','Control') NOT NULL default 'Events', `DefaultRate` smallint(5) unsigned NOT NULL default '100', `DefaultScale` smallint(5) unsigned NOT NULL default '100', + `DefaultCodec` enum('auto','MP4','MJPEG') NOT NULL default 'auto', `SignalCheckPoints` INT UNSIGNED NOT NULL default '0', `SignalCheckColour` varchar(32) NOT NULL default '#0000BE', `WebColour` varchar(32) NOT NULL default 'red', @@ -517,6 +527,7 @@ CREATE TABLE `Monitors` ( `ArchivedEvents` int(10) default NULL, `ArchivedEventDiskSpace` bigint default NULL, `ZoneCount` TINYINT NOT NULL DEFAULT 0, + `Refresh` int(10) unsigned default NULL, PRIMARY KEY (`Id`) ) ENGINE=@ZM_MYSQL_ENGINE@; @@ -555,7 +566,12 @@ INSERT INTO States (Name,Definition,IsActive) VALUES ('default','','1'); DROP TABLE IF EXISTS `Servers`; CREATE TABLE `Servers` ( `Id` int(10) unsigned NOT NULL auto_increment, + `Protocol` TEXT, `Hostname` TEXT, + `Port` INTEGER UNSIGNED, + `PathToIndex` TEXT, + `PathToZMS` TEXT, + `PathToApi` TEXT, `Name` varchar(64) NOT NULL default '', `State_Id` int(10) unsigned, `Status` enum('Unknown','NotRunning','Running') NOT NULL default 'Unknown', @@ -567,6 +583,7 @@ CREATE TABLE `Servers` ( `zmstats` BOOLEAN NOT NULL DEFAULT FALSE, `zmaudit` BOOLEAN NOT NULL DEFAULT FALSE, `zmtrigger` BOOLEAN NOT NULL DEFAULT FALSE, + `zmeventnotification` BOOLEAN NOT NULL DEFAULT FALSE, PRIMARY KEY (`Id`) ) ENGINE=@ZM_MYSQL_ENGINE@; @@ -578,9 +595,10 @@ CREATE INDEX `Servers_Name_idx` ON `Servers` (`Name`); DROP TABLE IF EXISTS `Stats`; CREATE TABLE `Stats` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT, `MonitorId` int(10) unsigned NOT NULL default '0', `ZoneId` int(10) unsigned NOT NULL default '0', - `EventId` int(10) unsigned NOT NULL default '0', + `EventId` BIGINT UNSIGNED NOT NULL, `FrameId` int(10) unsigned NOT NULL default '0', `PixelDiff` tinyint(3) unsigned NOT NULL default '0', `AlarmPixels` int(10) unsigned NOT NULL default '0', @@ -594,6 +612,7 @@ CREATE TABLE `Stats` ( `MinY` smallint(5) unsigned NOT NULL default '0', `MaxY` smallint(5) unsigned NOT NULL default '0', `Score` smallint(5) unsigned NOT NULL default '0', + PRIMARY KEY (`Id`), KEY `EventId` (`EventId`), KEY `MonitorId` (`MonitorId`), KEY `ZoneId` (`ZoneId`) @@ -631,7 +650,9 @@ CREATE TABLE `Users` ( `Devices` enum('None','View','Edit') NOT NULL default 'None', `System` enum('None','View','Edit') NOT NULL default 'None', `MaxBandwidth` varchar(16), - `MonitorIds` tinytext, + `MonitorIds` text, + `TokenMinExpiry` BIGINT UNSIGNED NOT NULL DEFAULT 0, + `APIEnabled` tinyint(3) UNSIGNED NOT NULL default 1, PRIMARY KEY (`Id`), UNIQUE KEY `UC_Username` (`Username`) ) ENGINE=@ZM_MYSQL_ENGINE@; @@ -732,51 +753,69 @@ insert into Storage VALUES (NULL, '@ZM_DIR_EVENTS@', 'Default', 'local', NULL, N -- -- Create a default admin user. -- -insert into Users VALUES (NULL,'admin',password('admin'),'',1,'View','Edit','Edit','Edit','Edit','Edit','Edit','',''); +insert into Users VALUES (NULL,'admin','$2b$12$NHZsm6AM2f2LQVROriz79ul3D6DnmFiZC.ZK5eqbF.ZWfwH9bqUJ6','',1,'View','Edit','Edit','Edit','Edit','Edit','Edit','','',0,1); -- -- 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 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/*AutoCopy*/,0/*CopyTo*/, + 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,0,0,1,1,0); -- -- Add in some sample control protocol definitions -- -INSERT INTO Controls VALUES (NULL,'Pelco-D','Local','PelcoD',1,1,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0); -INSERT INTO Controls VALUES (NULL,'Pelco-P','Local','PelcoP',1,1,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0); -INSERT INTO Controls VALUES (NULL,'Sony VISCA','Local','Visca',1,1,0,1,0,0,0,1,0,16384,10,4000,1,1,6,1,1,1,0,1,0,1536,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,3,1,1,1,1,0,1,1,0,1,-15578,15578,100,10000,1,1,50,1,254,1,-7789,7789,100,5000,1,1,50,1,254,0,0); -INSERT INTO Controls VALUES (NULL,'Axis API v2','Remote','AxisV2',0,0,0,1,0,0,1,0,0,9999,10,2500,0,NULL,NULL,1,1,0,1,0,0,9999,10,2500,0,NULL,NULL,1,1,0,1,0,0,9999,10,2500,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,12,1,1,1,1,1,0,1,0,1,-360,360,1,90,0,NULL,NULL,0,NULL,1,-360,360,1,90,0,NULL,NULL,0,NULL,0,0); -INSERT INTO Controls VALUES (NULL,'Panasonic IP','Remote','PanasonicIP',0,0,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,8,1,1,1,0,1,0,0,1,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0); -INSERT INTO Controls VALUES (NULL,'Neu-Fusion NCS370','Remote','Ncs370',0,0,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,24,1,0,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0); -INSERT INTO Controls VALUES (NULL,'AirLink SkyIPCam 7xx','Remote','SkyIPCam7xx',0,0,1,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,8,1,1,1,0,1,0,1,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0); -INSERT INTO Controls VALUES (NULL,'Pelco-D','Ffmpeg','PelcoD',1,1,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0); -INSERT INTO Controls VALUES (NULL,'Pelco-P','Ffmpeg','PelcoP',1,1,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0); -INSERT INTO Controls VALUES (NULL,'Foscam FI8620','Ffmpeg','FI8620_Y2k',0,0,0,1,0,0,0,1,1,10,1,10,1,1,63,1,1,0,0,1,1,63,1,63,1,1,63,1,1,0,0,1,0,0,0,0,1,0,255,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,255,1,8,0,1,1,1,0,0,0,1,1,1,360,1,360,1,1,63,0,0,1,1,90,1,90,1,1,63,0,0,0,0); -INSERT INTO Controls VALUES (NULL,'Foscam FI8608W','Ffmpeg','FI8608W_Y2k',1,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,255,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,255,1,8,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,128,0,0,1,0,0,0,0,1,1,128,0,0,0,0); -INSERT INTO Controls VALUES (NULL,'Foscam FI8908W','Remote','FI8908W',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,1,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); -INSERT INTO Controls VALUES (NULL,'Foscam FI9821W','Ffmpeg','FI9821W_Y2k',1,0,1,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,100,1,1,0,0,1,0,100,0,100,1,0,100,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,100,0,100,1,0,100,1,16,0,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,1,0,4,0,0,0,0); -INSERT INTO Controls VALUES (NULL,'Loftek Sentinel','Remote','LoftekSentinel',0,0,1,1,0,0,0,1,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,255,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,6,1,1,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); -INSERT INTO Controls VALUES (NULL,'Toshiba IK-WB11A','Remote','Toshiba_IK_WB11A',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,10,0,1,1,0,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); -INSERT INTO Controls VALUES (NULL,'WanscamPT','Remote','Wanscam',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,1,0,1,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,16,0,0,0,0,0,1,16,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); -INSERT INTO Controls VALUES (NULL,'3S Domo N5071', 'Remote', '3S', 0, 0, 1, 1, 0, 1, 1, 0, 0, 9999, 0, 9999, 0, 0, 0, 1, 1, 1, 1, 0, 0, 9999, 20, 9999, 0, 0, 0, 1, 1, 1, 1, 0, 0, 9999, 1, 9999, 0, 0, 0, 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, 64, 1, 0, 1, 1, 0, 0, 0, 0, 1, -180, 180, 40, 100, 1, 40, 100, 0, 0, 1, -180, 180, 40, 100, 1, 40, 100, 0, 0, 0, 0); -INSERT INTO Controls VALUES (NULL,'ONVIF Camera','Ffmpeg','onvif',0,0,1,1,0,0,0,1,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,255,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,6,1,1,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); -INSERT INTO `Controls` VALUES (NULL,'Foscam 9831W','Ffmpeg','FI9831W',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,16,1,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,0,0,0,0,0,0,0); -INSERT INTO `Controls` VALUES (NULL,'Foscam FI8918W','Ffmpeg','FI8918W',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,8,0,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,1,0,4,0,0,0,0); -INSERT INTO `Controls` VALUES (NULL,'SunEyes SP-P1802SWPTZ','Libvlc','SPP1802SWPTZ',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,8,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,64,0,0,1,0,0,0,0,1,0,64,0,0,0,0); -INSERT INTO `Controls` VALUES (NULL,'Wanscam HW0025','Libvlc','WanscamHW0025', 1, 1, 1, 1, 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, 1, 16, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 350, 0, 0, 1, 0, 10, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 0, 0, 0, 0); -INSERT INTO `Controls` VALUES (NULL,'IPCC 7210W','Remote','IPCC7210W', 1, 1, 1, 1, 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, 1, 16, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); -INSERT INTO `Controls` VALUES (NULL,'Vivotek ePTZ','Remote','Vivotek_ePTZ',0,0,1,1,0,0,0,1,0,0,0,0,1,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,1,1,0,0,0,0,1,0,5,0,0,1,0,0,0,0,1,0,5,0,0,0,0); -INSERT INTO `Controls` VALUES (NULL,'Netcat ONVIF','Ffmpeg','Netcat',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,100,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,100,5,5,0,0,0,1,255,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); -INSERT INTO `Controls` VALUES (NULL,'Keekoon','Remote','Keekoon', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); -INSERT INTO `Controls` VALUES (NULL,'HikVision','Local','',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,0,1,1,0,0,0,0,1,1,100,0,0,1,0,0,0,0,1,1,100,1,0,0,0); -INSERT INTO `Controls` VALUES (NULL,'Maginon Supra IPC','cURL','MaginonIPC',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,0,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); -INSERT INTO `Controls` VALUES (NULL,'Floureon 1080P','Ffmpeg','Floureon',0,0,0,1,0,0,0,1,1,18,1,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,1,1,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,1,20,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,8,0,0,1,0,0,0,0,1,1,8,0,0,0,0); -INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-423','Ffmpeg','Reolink',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,64,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); -INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-411','Ffmpeg','Reolink',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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); -INSERT INTO `Controls` VALUES (NULL,'D-LINK DCS-3415','Remote','DCS3415',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,'Pelco-D','Local','PelcoD',1,1,0,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0); +INSERT INTO Controls VALUES (NULL,'Pelco-P','Local','PelcoP',1,1,0,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0); +INSERT INTO Controls VALUES (NULL,'Sony VISCA','Local','Visca',1,1,0,0,1,0,0,0,1,0,16384,10,4000,1,1,6,1,1,1,0,1,0,1536,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,3,1,1,1,1,0,1,1,0,1,-15578,15578,100,10000,1,1,50,1,254,1,-7789,7789,100,5000,1,1,50,1,254,0,0); +INSERT INTO Controls VALUES (NULL,'Axis API v2','Remote','AxisV2',0,0,0,0,1,0,0,1,0,0,9999,10,2500,0,NULL,NULL,1,1,0,1,0,0,9999,10,2500,0,NULL,NULL,1,1,0,1,0,0,9999,10,2500,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,12,1,1,1,1,1,0,1,0,1,-360,360,1,90,0,NULL,NULL,0,NULL,1,-360,360,1,90,0,NULL,NULL,0,NULL,0,0); +INSERT INTO Controls VALUES (NULL,'Panasonic IP','Remote','PanasonicIP',0,0,0,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,8,1,1,1,0,1,0,0,1,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0); +INSERT INTO Controls VALUES (NULL,'Neu-Fusion NCS370','Remote','Ncs370',0,0,0,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,24,1,0,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0); +INSERT INTO Controls VALUES (NULL,'AirLink SkyIPCam 7xx','Remote','SkyIPCam7xx',0,0,1,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,8,1,1,1,0,1,0,1,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0); +INSERT INTO Controls VALUES (NULL,'Pelco-D','Ffmpeg','PelcoD',1,1,0,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0); +INSERT INTO Controls VALUES (NULL,'Pelco-P','Ffmpeg','PelcoP',1,1,0,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0); +INSERT INTO Controls VALUES (NULL,'Foscam FI8620','Ffmpeg','FI8620_Y2k',0,0,0,0,1,0,0,0,1,1,10,1,10,1,1,63,1,1,0,0,1,1,63,1,63,1,1,63,1,1,0,0,1,0,0,0,0,1,0,255,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,255,1,8,0,1,1,1,0,0,0,1,1,1,360,1,360,1,1,63,0,0,1,1,90,1,90,1,1,63,0,0,0,0); +INSERT INTO Controls VALUES (NULL,'Foscam FI8608W','Ffmpeg','FI8608W_Y2k',1,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,255,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,255,1,8,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,128,0,0,1,0,0,0,0,1,1,128,0,0,0,0); +INSERT INTO Controls VALUES (NULL,'Foscam FI8908W','Remote','FI8908W',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,1,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); +INSERT INTO Controls VALUES (NULL,'Foscam FI9821W','Ffmpeg','FI9821W_Y2k',1,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,100,1,1,0,0,1,0,100,0,100,1,0,100,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,100,0,100,1,0,100,1,16,0,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,1,0,4,0,0,0,0); +INSERT INTO Controls VALUES (NULL,'Loftek Sentinel','Remote','LoftekSentinel',0,0,1,0,1,0,0,0,1,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,255,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,6,1,1,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); +INSERT INTO Controls VALUES (NULL,'Toshiba IK-WB11A','Remote','Toshiba_IK_WB11A',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,1,10,0,1,1,0,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); +INSERT INTO Controls VALUES (NULL,'WanscamPT','Remote','Wanscam',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,1,0,1,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,16,0,0,0,0,0,1,16,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); +INSERT INTO Controls VALUES (NULL,'3S Domo N5071', 'Remote', '3S', 0, 0, 1, 0,1, 0, 1, 1, 0, 0, 9999, 0, 9999, 0, 0, 0, 1, 1, 1, 1, 0, 0, 9999, 20, 9999, 0, 0, 0, 1, 1, 1, 1, 0, 0, 9999, 1, 9999, 0, 0, 0, 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, 64, 1, 0, 1, 1, 0, 0, 0, 0, 1, -180, 180, 40, 100, 1, 40, 100, 0, 0, 1, -180, 180, 40, 100, 1, 40, 100, 0, 0, 0, 0); +INSERT INTO Controls VALUES (NULL,'ONVIF Camera','Ffmpeg','onvif',0,0,1,0,1,0,0,0,1,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,255,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,6,1,1,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'Foscam 9831W','Ffmpeg','FI9831W',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,1,16,1,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,0,0,0,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'Foscam FI8918W','Ffmpeg','FI8918W',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,1,8,0,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,1,0,4,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'SunEyes SP-P1802SWPTZ','Libvlc','SPP1802SWPTZ',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,8,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,64,0,0,1,0,0,0,0,1,0,64,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'Wanscam HW0025','Libvlc','WanscamHW0025', 1, 1, 1, 0,1, 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, 1, 16, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 350, 0, 0, 1, 0, 10, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 0, 0, 0, 0); +INSERT INTO `Controls` VALUES (NULL,'IPCC 7210W','Remote','IPCC7210W', 1, 1, 1, 0,1, 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, 1, 16, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `Controls` VALUES (NULL,'Vivotek ePTZ','Remote','Vivotek_ePTZ',0,0,1,0,1,0,0,0,1,0,0,0,0,1,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,1,1,0,0,0,0,1,0,5,0,0,1,0,0,0,0,1,0,5,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'Netcat ONVIF','Ffmpeg','Netcat',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,1,0,1,0,0,0,100,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,100,5,5,0,0,0,1,255,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'Keekoon','Remote','Keekoon', 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `Controls` VALUES (NULL,'HikVision','Local','',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,0,1,1,0,0,0,0,1,1,100,0,0,1,0,0,0,0,1,1,100,1,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'Maginon Supra IPC','cURL','MaginonIPC',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,0,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'Floureon 1080P','Ffmpeg','Floureon',0,0,0,0,1,0,0,0,1,1,18,1,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,1,1,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,1,20,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,8,0,0,1,0,0,0,0,1,1,8,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-423','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,1,64,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'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); +INSERT INTO `Controls` VALUES (NULL,'PSIA','Remote','PSIA',0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,0,1,1,1,0,0,1,0,1,0,0,0,0,1,-100,100,0,0,1,0,0,0,0,1,-100,100,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'Dahua','Ffmpeg','Dahua',0,0,1,1,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,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,1,20,1,1,1,1,0,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); +INSERT INTO `Controls` VALUES (NULL,'FOSCAMR2C','Libvlc','FOSCAMR2C',1,1,1,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,12,0,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,4,0,NULL,1,NULL,NULL,NULL,NULL,1,0,4,0,NULL,0,0); +INSERT INTO `Controls` VALUES (NULL,'Amcrest HTTP API','Ffmpeg','Amcrest_HTTP',0,0,1,0,1,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,1,5,0,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,5); -- -- Add some monitor preset values @@ -803,6 +842,8 @@ INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, unicast','Remote','rtsp INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, multicast','Remote','rtsp',0,255,'rtsp','rtpMulti','',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','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); @@ -819,6 +860,7 @@ INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg','Remote','http',0,0,' INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, mpjpeg','Remote','http',0,0,'http','simple','',80,'/GetData.cgi',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, mpjpeg','Remote','http',0,0,'http','simple','',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100); +INSERT INTO MonitorPresets VALUES (NULL,'IP Webcam by Pavel Khlebovich 1920x1080','Remote','/dev/video','0',255,'http','simple','','8080','/video','',1920,1080,0,NULL,0,'0','','',100,100); INSERT INTO MonitorPresets VALUES (NULL,'VEO Observer, jpeg','Remote','http',0,0,'http','simple','',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Blue Net Video Server, jpeg','Remote','http',0,0,'http','simple','',80,'/cgi-bin/image.cgi?control=0&id=admin&passwd=admin',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT into MonitorPresets VALUES (NULL,'ACTi IP, mpeg4, unicast','Remote',NULL,NULL,NULL,'rtsp','rtpUni','',7070,'','/track',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); diff --git a/db/zm_update-1.28.99.sql b/db/zm_update-1.28.99.sql index 5b342a811..ccbc9c059 100644 --- a/db/zm_update-1.28.99.sql +++ b/db/zm_update-1.28.99.sql @@ -322,7 +322,7 @@ WHERE NOT EXISTS ( -- -- Hide USE_DEEP_STORAGE from user to prevent accidental event loss -- -UPDATE `zm`.`Config` SET `Category`='hidden' WHERE `Name`='ZM_USE_DEEP_STORAGE'; +UPDATE Config SET Category='hidden' WHERE Name='ZM_USE_DEEP_STORAGE'; -- -- Add Id column to State diff --git a/db/zm_update-1.31.43.sql b/db/zm_update-1.31.43.sql index d8d6eaefd..c97405903 100644 --- a/db/zm_update-1.31.43.sql +++ b/db/zm_update-1.31.43.sql @@ -5,7 +5,7 @@ -- Add Refresh column to Monitors table -- -ALTER TABLE `zm`.`Monitors` +ALTER TABLE `Monitors` CHANGE COLUMN `Type` `Type` ENUM('Local', 'Remote', 'File', 'Ffmpeg', 'Libvlc', 'cURL', 'WebSite') NOT NULL DEFAULT 'Local' ; SET @s = (SELECT IF( @@ -16,7 +16,7 @@ SET @s = (SELECT IF( AND column_name = 'Refresh' ) > 0, "SELECT 'Column Refresh exists in Monitors'", -"ALTER TABLE Monitors ADD `Refresh` int(10) unsigned default NULL" +"ALTER TABLE Monitors ADD `Refresh` int(10) unsigned default NULL AFTER `ZoneCount`" )); PREPARE stmt FROM @s; diff --git a/db/zm_update-1.31.45.sql b/db/zm_update-1.31.45.sql new file mode 100644 index 000000000..c3be6c4cd --- /dev/null +++ b/db/zm_update-1.31.45.sql @@ -0,0 +1,23 @@ +-- +-- This updates a 1.31.44 database to 1.31.45 +-- +-- Add WebSite enum to Monitor.Type +-- Add Refresh column to Monitors table + +-- This is the same as the update to 1.31.43, but due to Refresh not being added to zm_create.sql.in we need to have it +-- again in order to fix people who did a fresh install from 1.31.43 or 1.31.44. +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'Refresh' + ) > 0, +"SELECT 'Column Refresh exists in Monitors'", +"ALTER TABLE Monitors ADD `Refresh` int(10) unsigned default NULL AFTER `ZoneCount`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.31.46.sql b/db/zm_update-1.31.46.sql new file mode 100644 index 000000000..8c9767e75 --- /dev/null +++ b/db/zm_update-1.31.46.sql @@ -0,0 +1,2 @@ +ALTER TABLE Stats MODIFY COLUMN EventId bigint unsigned NOT NULL; + diff --git a/db/zm_update-1.31.47.sql b/db/zm_update-1.31.47.sql new file mode 100644 index 000000000..714afd133 --- /dev/null +++ b/db/zm_update-1.31.47.sql @@ -0,0 +1 @@ +ALTER TABLE Frames MODIFY COLUMN EventId bigint unsigned NOT NULL; diff --git a/db/zm_update-1.32.0.sql b/db/zm_update-1.32.0.sql new file mode 100644 index 000000000..f0ff1719b --- /dev/null +++ b/db/zm_update-1.32.0.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.31.47 database to 1.32.0 +-- +-- No changes required +-- diff --git a/db/zm_update-1.32.1.sql b/db/zm_update-1.32.1.sql new file mode 100644 index 000000000..ed9a1879b --- /dev/null +++ b/db/zm_update-1.32.1.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.32.0 database to 1.32.1 +-- +-- No changes required +-- diff --git a/db/zm_update-1.32.2.sql b/db/zm_update-1.32.2.sql new file mode 100644 index 000000000..0bb156dad --- /dev/null +++ b/db/zm_update-1.32.2.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.32.1 database to 1.32.2 +-- +-- No changes required +-- diff --git a/db/zm_update-1.32.3.sql b/db/zm_update-1.32.3.sql new file mode 100644 index 000000000..6c3e2c47f --- /dev/null +++ b/db/zm_update-1.32.3.sql @@ -0,0 +1,362 @@ +-- +-- This updates a 1.32.2 database to 1.32.3 +-- + +delimiter // +DROP TRIGGER IF EXISTS Events_Hour_delete_trigger// +CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour +FOR EACH ROW BEGIN + UPDATE Monitors SET + HourEvents = COALESCE(HourEvents,1)-1, + HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) + WHERE Id=OLD.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS Events_Hour_update_trigger// + +CREATE TRIGGER Events_Hour_update_trigger AFTER UPDATE ON Events_Hour +FOR EACH ROW + BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( diff ) THEN + IF ( NEW.MonitorID != OLD.MonitorID ) THEN + UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId; + UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; + ELSE + UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId; + END IF; + END IF; + END; +// +DELIMITER ; + +delimiter // +DROP TRIGGER IF EXISTS Events_Day_delete_trigger// +CREATE TRIGGER Events_Day_delete_trigger BEFORE DELETE ON Events_Day +FOR EACH ROW BEGIN + UPDATE Monitors SET + DayEvents = COALESCE(DayEvents,1)-1, + DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) + WHERE Id=OLD.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS Events_Day_update_trigger; +CREATE TRIGGER Events_Day_update_trigger AFTER UPDATE ON Events_Day +FOR EACH ROW + BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( diff ) THEN + IF ( NEW.MonitorID != OLD.MonitorID ) THEN + UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId; + UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; + ELSE + UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId; + END IF; + END IF; + END; + // + + +DROP TRIGGER IF EXISTS Events_Week_delete_trigger// +CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week +FOR EACH ROW BEGIN + UPDATE Monitors SET + WeekEvents = COALESCE(WeekEvents,1)-1, + WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) + WHERE Id=OLD.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS Events_Week_update_trigger; +CREATE TRIGGER Events_Week_update_trigger AFTER UPDATE ON Events_Week +FOR EACH ROW + BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( diff ) THEN + IF ( NEW.MonitorID != OLD.MonitorID ) THEN + UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId; + UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; + ELSE + UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId; + END IF; + END IF; + END; + // + +DROP TRIGGER IF EXISTS Events_Month_delete_trigger// +CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month +FOR EACH ROW BEGIN + UPDATE Monitors SET + MonthEvents = COALESCE(MonthEvents,1)-1, + MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) + WHERE Id=OLD.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS Events_Month_update_trigger; +CREATE TRIGGER Events_Month_update_trigger AFTER UPDATE ON Events_Month +FOR EACH ROW + BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( diff ) THEN + IF ( NEW.MonitorID != OLD.MonitorID ) THEN + UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace) WHERE Monitors.Id=OLD.MonitorId; + UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+COALESCE(NEW.DiskSpace) WHERE Monitors.Id=NEW.MonitorId; + ELSE + UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId; + END IF; + END IF; + END; + // + +drop procedure if exists update_storage_stats// + +drop trigger if exists event_update_trigger// + +CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events +FOR EACH ROW +BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( NEW.StorageId = OLD.StorageID ) THEN + IF ( diff ) THEN + UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + diff WHERE Id = OLD.StorageId; + END IF; + ELSE + IF ( NEW.DiskSpace ) THEN + UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + NEW.DiskSpace WHERE Id = NEW.StorageId; + END IF; + IF ( OLD.DiskSpace ) THEN + UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) - OLD.DiskSpace WHERE Id = OLD.StorageId; + END IF; + END IF; + + UPDATE Events_Hour SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Events_Day SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Events_Week SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Events_Month SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + + IF ( NEW.Archived != OLD.Archived ) THEN + IF ( NEW.Archived ) THEN + INSERT INTO Events_Archived (EventId,MonitorId,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.DiskSpace); + UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=NEW.MonitorId; + ELSEIF ( OLD.Archived ) THEN + DELETE FROM Events_Archived WHERE EventId=OLD.Id; + UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)-1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) WHERE Id=OLD.MonitorId; + ELSE + IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN + UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Monitors SET + ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0) + WHERE Id=OLD.MonitorId; + END IF; + END IF; + ELSEIF ( NEW.Archived AND diff ) THEN + UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + END IF; + + IF ( diff ) THEN + UPDATE Monitors SET TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=OLD.MonitorId; + END IF; + +END; + +// + +delimiter ; + +DROP TRIGGER IF EXISTS event_insert_trigger; + +delimiter // +/* The assumption is that when an Event is inserted, it has no size yet, so don't bother updating the DiskSpace, just the count. + * The DiskSpace will get update in the Event Update Trigger + */ +CREATE TRIGGER event_insert_trigger AFTER INSERT ON Events +FOR EACH ROW + BEGIN + + INSERT INTO Events_Hour (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + INSERT INTO Events_Day (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + INSERT INTO Events_Week (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + INSERT INTO Events_Month (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + UPDATE Monitors SET + HourEvents = COALESCE(HourEvents,0)+1, + DayEvents = COALESCE(DayEvents,0)+1, + WeekEvents = COALESCE(WeekEvents,0)+1, + MonthEvents = COALESCE(MonthEvents,0)+1, + TotalEvents = COALESCE(TotalEvents,0)+1 + WHERE Id=NEW.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS event_delete_trigger// + +CREATE TRIGGER event_delete_trigger BEFORE DELETE ON Events +FOR EACH ROW +BEGIN + IF ( OLD.DiskSpace ) THEN + UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) - CAST(OLD.DiskSpace AS SIGNED) WHERE Id = OLD.StorageId; + END IF; + DELETE FROM Events_Hour WHERE EventId=OLD.Id; + DELETE FROM Events_Day WHERE EventId=OLD.Id; + DELETE FROM Events_Week WHERE EventId=OLD.Id; + DELETE FROM Events_Month WHERE EventId=OLD.Id; + IF ( OLD.Archived ) THEN + DELETE FROM Events_Archived WHERE EventId=OLD.Id; + UPDATE Monitors SET + ArchivedEvents = COALESCE(ArchivedEvents,1) - 1, + ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0), + TotalEvents = COALESCE(TotalEvents,1) - 1, + TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + WHERE Id=OLD.MonitorId; + ELSE + UPDATE Monitors SET + TotalEvents = COALESCE(TotalEvents,1)-1, + TotalEventDiskSpace=COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) + WHERE Id=OLD.MonitorId; + END IF; +END; + +// + +DROP TRIGGER IF EXISTS Zone_Insert_Trigger// +CREATE TRIGGER Zone_Insert_Trigger AFTER INSERT ON Zones +FOR EACH ROW + BEGIN + UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=NEW.MonitorId) WHERE Id=NEW.MonitorID; + END +// +DROP TRIGGER IF EXISTS Zone_Delete_Trigger// +CREATE TRIGGER Zone_Delete_Trigger AFTER DELETE ON Zones +FOR EACH ROW + BEGIN + UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=OLD.MonitorId) WHERE Id=OLD.MonitorID; + END +// + +DELIMITER ; + +REPLACE INTO Events_Day SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 day); +REPLACE INTO Events_Week SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 week); +REPLACE INTO Events_Month SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 month); +REPLACE INTO Events_Archived SELECT Id,MonitorId,DiskSpace FROM Events WHERE Archived=1; + +UPDATE Monitors INNER JOIN ( + SELECT MonitorId, + COUNT(Id) AS TotalEvents, + SUM(DiskSpace) AS TotalEventDiskSpace, + SUM(IF(Archived,1,0)) AS ArchivedEvents, + SUM(IF(Archived,DiskSpace,0)) AS ArchivedEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),1,0)) AS HourEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),DiskSpace,0)) AS HourEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),1,0)) AS DayEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),DiskSpace,0)) AS DayEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),1,0)) AS WeekEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),DiskSpace,0)) AS WeekEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),1,0)) AS MonthEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),DiskSpace,0)) AS MonthEventDiskSpace + FROM Events GROUP BY MonitorId + ) AS E ON E.MonitorId=Monitors.Id SET + Monitors.TotalEvents = E.TotalEvents, + Monitors.TotalEventDiskSpace = E.TotalEventDiskSpace, + Monitors.ArchivedEvents = E.ArchivedEvents, + Monitors.ArchivedEventDiskSpace = E.ArchivedEventDiskSpace, + Monitors.HourEvents = E.HourEvents, + Monitors.HourEventDiskSpace = E.HourEventDiskSpace, + Monitors.DayEvents = E.DayEvents, + Monitors.DayEventDiskSpace = E.DayEventDiskSpace, + Monitors.WeekEvents = E.WeekEvents, + Monitors.WeekEventDiskSpace = E.WeekEventDiskSpace, + Monitors.MonthEvents = E.MonthEvents, + Monitors.MonthEventDiskSpace = E.MonthEventDiskSpace; + +-- +-- Add Protocol column to Storage +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Servers' + AND column_name = 'Protocol' + ) > 0, +"SELECT 'Column Protocol already exists in Servers'", +"ALTER TABLE Servers ADD `Protocol` TEXT AFTER `Id`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +-- +-- Add PathToIndex column to Storage +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Servers' + AND column_name = 'PathToIndex' + ) > 0, +"SELECT 'Column PathToIndex already exists in Servers'", +"ALTER TABLE Servers ADD `PathToIndex` TEXT AFTER `Hostname`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +-- +-- Add PathToZMS column to Storage +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Servers' + AND column_name = 'PathToZMS' + ) > 0, +"SELECT 'Column PathToZMS already exists in Servers'", +"ALTER TABLE Servers ADD `PathToZMS` TEXT AFTER `PathToIndex`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +-- +-- Add PathToApi column to Storage +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Servers' + AND column_name = 'PathToApi' + ) > 0, +"SELECT 'Column PathToApi already exists in Servers'", +"ALTER TABLE Servers ADD `PathToApi` TEXT AFTER `PathToZMS`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +-- +-- Add Port column to Storage +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Servers' + AND column_name = 'Port' + ) > 0, +"SELECT 'Column Port already exists in Servers'", +"ALTER TABLE Servers ADD `Port` INTEGER UNSIGNED AFTER `Hostname`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.33.0.sql b/db/zm_update-1.33.0.sql new file mode 100644 index 000000000..cc7a257c7 --- /dev/null +++ b/db/zm_update-1.33.0.sql @@ -0,0 +1,21 @@ +-- +-- This updates a 1.32.3 database to 1.33.0 +-- +-- +-- Remove DefaultView from Monitors table. +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'DefaultView' + ) > 0, +"ALTER TABLE Monitors DROP COLUMN DefaultView", +"SELECT 'Column DefaultView no longer exists in Monitors'" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + diff --git a/db/zm_update-1.33.1.sql b/db/zm_update-1.33.1.sql new file mode 100644 index 000000000..ddac8adae --- /dev/null +++ b/db/zm_update-1.33.1.sql @@ -0,0 +1,16 @@ +-- +-- This updates a 1.33.0 database to 1.33.1 +-- +-- Add WebSite enum to Monitor.Type +-- Add Refresh column to Monitors table +-- + +ALTER TABLE `Monitors` +CHANGE COLUMN `Type` `Type` ENUM('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL DEFAULT 'Local' ; + +ALTER TABLE `MonitorPresets` +CHANGE COLUMN `Type` `Type` ENUM('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL DEFAULT 'Local' ; + +ALTER TABLE `Controls` +CHANGE COLUMN `Type` `Type` ENUM('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL DEFAULT 'Local' ; + diff --git a/db/zm_update-1.33.10.sql b/db/zm_update-1.33.10.sql new file mode 100644 index 000000000..8eb8fc548 --- /dev/null +++ b/db/zm_update-1.33.10.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 = 'MinSectionLength' + ) > 0, + "SELECT 'Column MinSectionLength already exists in Monitors'", + "ALTER TABLE Monitors ADD `MinSectionLength` int(10) unsigned NOT NULL default '10' AFTER SectionLength" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.33.11.sql b/db/zm_update-1.33.11.sql new file mode 100644 index 000000000..57b04ce11 --- /dev/null +++ b/db/zm_update-1.33.11.sql @@ -0,0 +1,24 @@ + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'DecoderHWAccelName' + ) > 0, + "SELECT 'Column DecoderHWAccelName already exists in Monitors'", + "ALTER TABLE Monitors ADD `DecoderHWAccelName` varchar(64) AFTER `Deinterlacing`" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'DecoderHWAccelDevice' + ) > 0, + "SELECT 'Column DecoderHWAccelDevice already exists in Monitors'", + "ALTER TABLE Monitors ADD `DecoderHWAccelDevice` varchar(255) AFTER `DecoderHWAccelName`" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.33.12.sql b/db/zm_update-1.33.12.sql new file mode 100644 index 000000000..8188ad841 --- /dev/null +++ b/db/zm_update-1.33.12.sql @@ -0,0 +1,27 @@ +-- +-- Add primary keys for Logs and Stats tables +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Logs' + AND column_name = 'Id' + ) > 0, +"SELECT 'Column Id already exists in Logs'", +"ALTER TABLE `Logs` ADD COLUMN `Id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (`Id`)" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Stats' + AND column_name = 'Id' + ) > 0, +"SELECT 'Column Id already exists in Stats'", +"ALTER TABLE `Stats` ADD COLUMN `Id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (`Id`)" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.33.13.sql b/db/zm_update-1.33.13.sql new file mode 100644 index 000000000..8114205c0 --- /dev/null +++ b/db/zm_update-1.33.13.sql @@ -0,0 +1,6 @@ +-- +-- Add primary keys for Logs and Stats tables +-- + +SELECT "Modifying Monitors MaxFPS to DECIMAL(5,3)"; +ALTER TABLE `Monitors` MODIFY `MaxFPS` decimal(5,3) default NULL; diff --git a/db/zm_update-1.33.14.sql b/db/zm_update-1.33.14.sql new file mode 100644 index 000000000..83d0cfbba --- /dev/null +++ b/db/zm_update-1.33.14.sql @@ -0,0 +1,51 @@ +-- +-- Add CopyTo action to Filters +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Filters' + AND column_name = 'AutoCopy' + ) > 0, +"SELECT 'Column AutoCopy already exists in Filters'", +"ALTER TABLE Filters ADD `AutoCopy` tinyint(3) unsigned NOT NULL default '0' AFTER `AutoMove`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Filters' + AND column_name = 'AutoCopyTo' + ) > 0, +"SELECT 'Column AutoCopyTo already exists in Filters'", +"ALTER TABLE Filters ADD `AutoCopyTo` smallint(5) unsigned NOT NULL default '0' AFTER `AutoCopy`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Filters' + AND column_name = 'Query_json' + ) > 0, +"SELECT 'Column Query_json already exists in Filters'", +"ALTER TABLE `Filters` Change `Query` `Query_json` text NOT NULL" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Events' + AND column_name = 'SecondaryStorageId' + ) > 0, +"SELECT 'Column SecondaryStorageId already exists in Events'", +"ALTER TABLE `Events` ADD `SecondaryStorageId` smallint(5) unsigned default 0 AFTER `StorageId`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; 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.33.2.sql b/db/zm_update-1.33.2.sql new file mode 100644 index 000000000..ea0237b7e --- /dev/null +++ b/db/zm_update-1.33.2.sql @@ -0,0 +1,13 @@ +-- +-- This updates a 1.33.0 database to 1.33.1 +-- +-- Add WebSite enum to Monitor.Type +-- Add Refresh column to Monitors table +-- + +ALTER TABLE `Events_Hour` MODIFY DiskSpace BIGINT default NULL; +ALTER TABLE `Events_Day` MODIFY DiskSpace BIGINT default NULL; +ALTER TABLE `Events_Week` MODIFY DiskSpace BIGINT default NULL; +ALTER TABLE `Events_Month` MODIFY DiskSpace BIGINT default NULL; +ALTER TABLE `Events_Archived` MODIFY DiskSpace BIGINT default NULL; + diff --git a/db/zm_update-1.33.3.sql b/db/zm_update-1.33.3.sql new file mode 100644 index 000000000..5488f809d --- /dev/null +++ b/db/zm_update-1.33.3.sql @@ -0,0 +1,12 @@ + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Servers' + AND column_name = 'zmeventnotification' + ) > 0, + "SELECT 'Column zmeventnotification already exists in Servers'", + "ALTER TABLE Servers ADD `zmeventnotification` BOOLEAN NOT NULL DEFAULT FALSE AFTER `zmtrigger`" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.33.4.sql b/db/zm_update-1.33.4.sql new file mode 100644 index 000000000..d9b498bc8 --- /dev/null +++ b/db/zm_update-1.33.4.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 = 'DefaultCodec' + ) > 0, + "SELECT 'Column DefaultCodec already exists in Monitors'", + "ALTER TABLE Monitors ADD `DefaultCodec` enum('auto','MP4','MJPEG') NOT NULL default 'auto' AFTER `DefaultScale`" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.33.5.sql b/db/zm_update-1.33.5.sql new file mode 100644 index 000000000..39400b5d8 --- /dev/null +++ b/db/zm_update-1.33.5.sql @@ -0,0 +1,12 @@ + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Controls' + AND column_name = 'CanReboot' + ) > 0, + "SELECT 'Column CanReboot already exists in Controls'", + "ALTER TABLE Controls ADD `CanReboot` tinyint(3) unsigned NOT NULL default '0' AFTER `CanReset`" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.33.6.sql b/db/zm_update-1.33.6.sql new file mode 100644 index 000000000..159f80ac6 --- /dev/null +++ b/db/zm_update-1.33.6.sql @@ -0,0 +1 @@ +ALTER TABLE Users MODIFY MonitorIds text; diff --git a/db/zm_update-1.33.7.sql b/db/zm_update-1.33.7.sql new file mode 100644 index 000000000..b29fe0e88 --- /dev/null +++ b/db/zm_update-1.33.7.sql @@ -0,0 +1,283 @@ + +delimiter // +DROP TRIGGER IF EXISTS Events_Hour_delete_trigger// +CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour +FOR EACH ROW BEGIN + UPDATE Monitors SET + HourEvents = COALESCE(HourEvents,1)-1, + HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS Events_Hour_update_trigger// + +CREATE TRIGGER Events_Hour_update_trigger AFTER UPDATE ON Events_Hour +FOR EACH ROW + BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( diff ) THEN + IF ( NEW.MonitorID != OLD.MonitorID ) THEN + UPDATE Monitors SET HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId; + UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; + ELSE + UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId; + END IF; + END IF; + END; +// + +DROP TRIGGER IF EXISTS Events_Day_delete_trigger// +CREATE TRIGGER Events_Day_delete_trigger BEFORE DELETE ON Events_Day +FOR EACH ROW BEGIN + UPDATE Monitors SET + DayEvents = GREATEST(COALESCE(DayEvents,1)-1,0), + DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS Events_Day_update_trigger; +CREATE TRIGGER Events_Day_update_trigger AFTER UPDATE ON Events_Day +FOR EACH ROW + BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( diff ) THEN + IF ( NEW.MonitorID != OLD.MonitorID ) THEN + UPDATE Monitors SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId; + UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; + ELSE + UPDATE Monitors SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId; + END IF; + END IF; + END; + // + + +DROP TRIGGER IF EXISTS Events_Week_delete_trigger// +CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week +FOR EACH ROW BEGIN + UPDATE Monitors SET + WeekEvents = COALESCE(WeekEvents,1)-1, + WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS Events_Week_update_trigger; +CREATE TRIGGER Events_Week_update_trigger AFTER UPDATE ON Events_Week +FOR EACH ROW + BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( diff ) THEN + IF ( NEW.MonitorID != OLD.MonitorID ) THEN + UPDATE Monitors SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId; + UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; + ELSE + UPDATE Monitors SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId; + END IF; + END IF; + END; + // + +DROP TRIGGER IF EXISTS Events_Month_delete_trigger// +CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month +FOR EACH ROW BEGIN + UPDATE Monitors SET + MonthEvents = COALESCE(MonthEvents,1)-1, + MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS Events_Month_update_trigger; +CREATE TRIGGER Events_Month_update_trigger AFTER UPDATE ON Events_Month +FOR EACH ROW + BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( diff ) THEN + IF ( NEW.MonitorID != OLD.MonitorID ) THEN + UPDATE Monitors SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace),0) WHERE Monitors.Id=OLD.MonitorId; + UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+COALESCE(NEW.DiskSpace) WHERE Monitors.Id=NEW.MonitorId; + ELSE + UPDATE Monitors SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId; + END IF; + END IF; + END; + // + +drop procedure if exists update_storage_stats// + +drop trigger if exists event_update_trigger// + +CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events +FOR EACH ROW +BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( NEW.StorageId = OLD.StorageID ) THEN + IF ( diff ) THEN + UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) + diff,0) WHERE Id = OLD.StorageId; + END IF; + ELSE + IF ( NEW.DiskSpace ) THEN + UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + NEW.DiskSpace WHERE Id = NEW.StorageId; + END IF; + IF ( OLD.DiskSpace ) THEN + UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - OLD.DiskSpace,0) WHERE Id = OLD.StorageId; + END IF; + END IF; + + UPDATE Events_Hour SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Events_Day SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Events_Week SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Events_Month SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + + IF ( NEW.Archived != OLD.Archived ) THEN + IF ( NEW.Archived ) THEN + INSERT INTO Events_Archived (EventId,MonitorId,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.DiskSpace); + UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=NEW.MonitorId; + ELSEIF ( OLD.Archived ) THEN + DELETE FROM Events_Archived WHERE EventId=OLD.Id; + UPDATE Monitors + SET + ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,0)-1,0), + ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; + ELSE + IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN + UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Monitors SET + ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; + END IF; + END IF; + ELSEIF ( NEW.Archived AND diff ) THEN + UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + END IF; + + IF ( diff ) THEN + UPDATE Monitors + SET + TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; + END IF; + +END; + +// + +DROP TRIGGER IF EXISTS event_insert_trigger// + +/* The assumption is that when an Event is inserted, it has no size yet, so don't bother updating the DiskSpace, just the count. + * The DiskSpace will get update in the Event Update Trigger + */ +CREATE TRIGGER event_insert_trigger AFTER INSERT ON Events +FOR EACH ROW + BEGIN + + INSERT INTO Events_Hour (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + INSERT INTO Events_Day (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + INSERT INTO Events_Week (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + INSERT INTO Events_Month (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + UPDATE Monitors SET + HourEvents = COALESCE(HourEvents,0)+1, + DayEvents = COALESCE(DayEvents,0)+1, + WeekEvents = COALESCE(WeekEvents,0)+1, + MonthEvents = COALESCE(MonthEvents,0)+1, + TotalEvents = COALESCE(TotalEvents,0)+1 + WHERE Id=NEW.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS event_delete_trigger// + +CREATE TRIGGER event_delete_trigger BEFORE DELETE ON Events +FOR EACH ROW +BEGIN + IF ( OLD.DiskSpace ) THEN + UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) WHERE Id = OLD.StorageId; + END IF; + DELETE FROM Events_Hour WHERE EventId=OLD.Id; + DELETE FROM Events_Day WHERE EventId=OLD.Id; + DELETE FROM Events_Week WHERE EventId=OLD.Id; + DELETE FROM Events_Month WHERE EventId=OLD.Id; + IF ( OLD.Archived ) THEN + DELETE FROM Events_Archived WHERE EventId=OLD.Id; + UPDATE Monitors SET + ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,1) - 1,0), + ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0), + TotalEvents = GREATEST(COALESCE(TotalEvents,1) - 1,0), + TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; + ELSE + UPDATE Monitors SET + TotalEvents = GREATEST(COALESCE(TotalEvents,1)-1,0), + TotalEventDiskSpace=GREATEST(COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; + END IF; +END; + +// + +DROP TRIGGER IF EXISTS Zone_Insert_Trigger// +CREATE TRIGGER Zone_Insert_Trigger AFTER INSERT ON Zones +FOR EACH ROW + BEGIN + UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=NEW.MonitorId) WHERE Id=NEW.MonitorID; + END +// +DROP TRIGGER IF EXISTS Zone_Delete_Trigger// +CREATE TRIGGER Zone_Delete_Trigger AFTER DELETE ON Zones +FOR EACH ROW + BEGIN + UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=OLD.MonitorId) WHERE Id=OLD.MonitorID; + END +// + +DELIMITER ; + +REPLACE INTO Events_Hour SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 hour); +REPLACE INTO Events_Day SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 day); +REPLACE INTO Events_Week SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 week); +REPLACE INTO Events_Month SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 month); +REPLACE INTO Events_Archived SELECT Id,MonitorId,DiskSpace FROM Events WHERE Archived=1; + +UPDATE Monitors INNER JOIN ( + SELECT MonitorId, + COUNT(Id) AS TotalEvents, + SUM(DiskSpace) AS TotalEventDiskSpace, + SUM(IF(Archived,1,0)) AS ArchivedEvents, + SUM(IF(Archived,DiskSpace,0)) AS ArchivedEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),1,0)) AS HourEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),DiskSpace,0)) AS HourEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),1,0)) AS DayEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),DiskSpace,0)) AS DayEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),1,0)) AS WeekEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),DiskSpace,0)) AS WeekEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),1,0)) AS MonthEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),DiskSpace,0)) AS MonthEventDiskSpace + FROM Events GROUP BY MonitorId + ) AS E ON E.MonitorId=Monitors.Id SET + Monitors.TotalEvents = E.TotalEvents, + Monitors.TotalEventDiskSpace = E.TotalEventDiskSpace, + Monitors.ArchivedEvents = E.ArchivedEvents, + Monitors.ArchivedEventDiskSpace = E.ArchivedEventDiskSpace, + Monitors.HourEvents = E.HourEvents, + Monitors.HourEventDiskSpace = E.HourEventDiskSpace, + Monitors.DayEvents = E.DayEvents, + Monitors.DayEventDiskSpace = E.DayEventDiskSpace, + Monitors.WeekEvents = E.WeekEvents, + Monitors.WeekEventDiskSpace = E.WeekEventDiskSpace, + Monitors.MonthEvents = E.MonthEvents, + Monitors.MonthEventDiskSpace = E.MonthEventDiskSpace; + diff --git a/db/zm_update-1.33.8.sql b/db/zm_update-1.33.8.sql new file mode 100644 index 000000000..a3fed9629 --- /dev/null +++ b/db/zm_update-1.33.8.sql @@ -0,0 +1,283 @@ + +delimiter // +DROP TRIGGER IF EXISTS Events_Hour_delete_trigger// +CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour +FOR EACH ROW BEGIN + UPDATE Monitors SET + HourEvents = GREATEST(COALESCE(HourEvents,1)-1,0), + HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS Events_Hour_update_trigger// + +CREATE TRIGGER Events_Hour_update_trigger AFTER UPDATE ON Events_Hour +FOR EACH ROW + BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( diff ) THEN + IF ( NEW.MonitorID != OLD.MonitorID ) THEN + UPDATE Monitors SET HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId; + UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; + ELSE + UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId; + END IF; + END IF; + END; +// + +DROP TRIGGER IF EXISTS Events_Day_delete_trigger// +CREATE TRIGGER Events_Day_delete_trigger BEFORE DELETE ON Events_Day +FOR EACH ROW BEGIN + UPDATE Monitors SET + DayEvents = GREATEST(COALESCE(DayEvents,1)-1,0), + DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS Events_Day_update_trigger; +CREATE TRIGGER Events_Day_update_trigger AFTER UPDATE ON Events_Day +FOR EACH ROW + BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( diff ) THEN + IF ( NEW.MonitorID != OLD.MonitorID ) THEN + UPDATE Monitors SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId; + UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; + ELSE + UPDATE Monitors SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId; + END IF; + END IF; + END; + // + + +DROP TRIGGER IF EXISTS Events_Week_delete_trigger// +CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week +FOR EACH ROW BEGIN + UPDATE Monitors SET + WeekEvents = GREATEST(COALESCE(WeekEvents,1)-1,0), + WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS Events_Week_update_trigger; +CREATE TRIGGER Events_Week_update_trigger AFTER UPDATE ON Events_Week +FOR EACH ROW + BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( diff ) THEN + IF ( NEW.MonitorID != OLD.MonitorID ) THEN + UPDATE Monitors SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId; + UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; + ELSE + UPDATE Monitors SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId; + END IF; + END IF; + END; + // + +DROP TRIGGER IF EXISTS Events_Month_delete_trigger// +CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month +FOR EACH ROW BEGIN + UPDATE Monitors SET + MonthEvents = GREATEST(COALESCE(MonthEvents,1)-1,0), + MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS Events_Month_update_trigger; +CREATE TRIGGER Events_Month_update_trigger AFTER UPDATE ON Events_Month +FOR EACH ROW + BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( diff ) THEN + IF ( NEW.MonitorID != OLD.MonitorID ) THEN + UPDATE Monitors SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace),0) WHERE Monitors.Id=OLD.MonitorId; + UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+COALESCE(NEW.DiskSpace) WHERE Monitors.Id=NEW.MonitorId; + ELSE + UPDATE Monitors SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId; + END IF; + END IF; + END; + // + +drop procedure if exists update_storage_stats// + +drop trigger if exists event_update_trigger// + +CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events +FOR EACH ROW +BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( NEW.StorageId = OLD.StorageID ) THEN + IF ( diff ) THEN + UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) + diff,0) WHERE Id = OLD.StorageId; + END IF; + ELSE + IF ( NEW.DiskSpace ) THEN + UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + NEW.DiskSpace WHERE Id = NEW.StorageId; + END IF; + IF ( OLD.DiskSpace ) THEN + UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - OLD.DiskSpace,0) WHERE Id = OLD.StorageId; + END IF; + END IF; + + UPDATE Events_Hour SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Events_Day SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Events_Week SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Events_Month SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + + IF ( NEW.Archived != OLD.Archived ) THEN + IF ( NEW.Archived ) THEN + INSERT INTO Events_Archived (EventId,MonitorId,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.DiskSpace); + UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=NEW.MonitorId; + ELSEIF ( OLD.Archived ) THEN + DELETE FROM Events_Archived WHERE EventId=OLD.Id; + UPDATE Monitors + SET + ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,0)-1,0), + ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; + ELSE + IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN + UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Monitors SET + ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; + END IF; + END IF; + ELSEIF ( NEW.Archived AND diff ) THEN + UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + END IF; + + IF ( diff ) THEN + UPDATE Monitors + SET + TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; + END IF; + +END; + +// + +DROP TRIGGER IF EXISTS event_insert_trigger// + +/* The assumption is that when an Event is inserted, it has no size yet, so don't bother updating the DiskSpace, just the count. + * The DiskSpace will get update in the Event Update Trigger + */ +CREATE TRIGGER event_insert_trigger AFTER INSERT ON Events +FOR EACH ROW + BEGIN + + INSERT INTO Events_Hour (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + INSERT INTO Events_Day (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + INSERT INTO Events_Week (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + INSERT INTO Events_Month (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + UPDATE Monitors SET + HourEvents = COALESCE(HourEvents,0)+1, + DayEvents = COALESCE(DayEvents,0)+1, + WeekEvents = COALESCE(WeekEvents,0)+1, + MonthEvents = COALESCE(MonthEvents,0)+1, + TotalEvents = COALESCE(TotalEvents,0)+1 + WHERE Id=NEW.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS event_delete_trigger// + +CREATE TRIGGER event_delete_trigger BEFORE DELETE ON Events +FOR EACH ROW +BEGIN + IF ( OLD.DiskSpace ) THEN + UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) WHERE Id = OLD.StorageId; + END IF; + DELETE FROM Events_Hour WHERE EventId=OLD.Id; + DELETE FROM Events_Day WHERE EventId=OLD.Id; + DELETE FROM Events_Week WHERE EventId=OLD.Id; + DELETE FROM Events_Month WHERE EventId=OLD.Id; + IF ( OLD.Archived ) THEN + DELETE FROM Events_Archived WHERE EventId=OLD.Id; + UPDATE Monitors SET + ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,1) - 1,0), + ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0), + TotalEvents = GREATEST(COALESCE(TotalEvents,1) - 1,0), + TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; + ELSE + UPDATE Monitors SET + TotalEvents = GREATEST(COALESCE(TotalEvents,1)-1,0), + TotalEventDiskSpace=GREATEST(COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; + END IF; +END; + +// + +DROP TRIGGER IF EXISTS Zone_Insert_Trigger// +CREATE TRIGGER Zone_Insert_Trigger AFTER INSERT ON Zones +FOR EACH ROW + BEGIN + UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=NEW.MonitorId) WHERE Id=NEW.MonitorID; + END +// +DROP TRIGGER IF EXISTS Zone_Delete_Trigger// +CREATE TRIGGER Zone_Delete_Trigger AFTER DELETE ON Zones +FOR EACH ROW + BEGIN + UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=OLD.MonitorId) WHERE Id=OLD.MonitorID; + END +// + +DELIMITER ; + +REPLACE INTO Events_Hour SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 hour); +REPLACE INTO Events_Day SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 day); +REPLACE INTO Events_Week SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 week); +REPLACE INTO Events_Month SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 month); +REPLACE INTO Events_Archived SELECT Id,MonitorId,DiskSpace FROM Events WHERE Archived=1; + +UPDATE Monitors INNER JOIN ( + SELECT MonitorId, + COUNT(Id) AS TotalEvents, + SUM(DiskSpace) AS TotalEventDiskSpace, + SUM(IF(Archived,1,0)) AS ArchivedEvents, + SUM(IF(Archived,DiskSpace,0)) AS ArchivedEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),1,0)) AS HourEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),DiskSpace,0)) AS HourEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),1,0)) AS DayEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),DiskSpace,0)) AS DayEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),1,0)) AS WeekEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),DiskSpace,0)) AS WeekEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),1,0)) AS MonthEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),DiskSpace,0)) AS MonthEventDiskSpace + FROM Events GROUP BY MonitorId + ) AS E ON E.MonitorId=Monitors.Id SET + Monitors.TotalEvents = E.TotalEvents, + Monitors.TotalEventDiskSpace = E.TotalEventDiskSpace, + Monitors.ArchivedEvents = E.ArchivedEvents, + Monitors.ArchivedEventDiskSpace = E.ArchivedEventDiskSpace, + Monitors.HourEvents = E.HourEvents, + Monitors.HourEventDiskSpace = E.HourEventDiskSpace, + Monitors.DayEvents = E.DayEvents, + Monitors.DayEventDiskSpace = E.DayEventDiskSpace, + Monitors.WeekEvents = E.WeekEvents, + Monitors.WeekEventDiskSpace = E.WeekEventDiskSpace, + Monitors.MonthEvents = E.MonthEvents, + Monitors.MonthEventDiskSpace = E.MonthEventDiskSpace; + diff --git a/db/zm_update-1.33.9.sql b/db/zm_update-1.33.9.sql new file mode 100644 index 000000000..e0d289ba4 --- /dev/null +++ b/db/zm_update-1.33.9.sql @@ -0,0 +1,27 @@ +-- +-- Add per user API enable/disable and ability to set a minimum issued time for tokens +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Users' + AND column_name = 'TokenMinExpiry' + ) > 0, +"SELECT 'Column TokenMinExpiry already exists in Users'", +"ALTER TABLE Users ADD `TokenMinExpiry` BIGINT UNSIGNED NOT NULL DEFAULT 0 AFTER `MonitorIds`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Users' + AND column_name = 'APIEnabled' + ) > 0, +"SELECT 'Column APIEnabled already exists in Users'", +"ALTER TABLE Users ADD `APIEnabled` tinyint(3) UNSIGNED NOT NULL default 1 AFTER `TokenMinExpiry`" +)); + +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.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/description-pak b/description-pak deleted file mode 100644 index 7afaa14a4..000000000 --- a/description-pak +++ /dev/null @@ -1 +0,0 @@ -Zoneminder with kfir performances patches and fixes from UnixMedia (nextime) diff --git a/distros/debian/control b/distros/debian/control index 9de030e87..6bb59f206 100644 --- a/distros/debian/control +++ b/distros/debian/control @@ -13,6 +13,7 @@ Build-Depends: debhelper (>= 9), cmake , libv4l-dev (>= 0.8.3) , libbz2-dev , ffmpeg | libav-tools + , net-tools , libnetpbm10-dev , libvlccore-dev, libvlc-dev , libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev @@ -20,11 +21,13 @@ Build-Depends: debhelper (>= 9), cmake , libphp-serialization-perl , libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, libdbd-mysql-perl , libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl - , libmodule-load-perl, libsys-mmap-perl, libjson-any-perl + , libmodule-load-perl, libsys-mmap-perl, libjson-maybexs-perl , libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl , libdata-dump-perl, libclass-std-fast-perl, libsoap-wsdl-perl, libio-socket-multicast-perl, libdigest-sha-perl , libsys-cpu-perl, libsys-meminfo-perl , libdata-uuid-perl + , libssl-dev + , libcrypt-eksblowfish-perl, libdata-entropy-perl Standards-Version: 3.9.4 Package: zoneminder @@ -36,11 +39,13 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} , libphp-serialization-perl , libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, libdbd-mysql-perl , libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl - , libmodule-load-perl, libsys-mmap-perl, libjson-any-perl + , libmodule-load-perl, libsys-mmap-perl, libjson-maybexs-perl , libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl , libdata-dump-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 , libpcre3 , ffmpeg | libav-tools, libavdevice53 | libavdevice55 | libavdevice57 , rsyslog | system-log-daemon @@ -48,6 +53,9 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} , zip , libvlccore5 | libvlccore7 | libvlccore8, libvlc5 , libpolkit-gobject-1-0, php5-gd + , libssl + ,libcrypt-eksblowfish-perl, libdata-entropy-perl + Recommends: mysql-server | mariadb-server Description: Video camera security and surveillance solution ZoneMinder is intended for use in single or multi-camera video security diff --git a/distros/debian/postinst b/distros/debian/postinst index 3cd3fd277..36472436a 100644 --- a/distros/debian/postinst +++ b/distros/debian/postinst @@ -31,6 +31,10 @@ if [ "$1" = "configure" ]; then # test if database if already present... if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf + if [ $? -ne 0 ]; then + echo "Error creating db." + exit 1; + fi # This creates the user. echo "grant lock tables, alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql else diff --git a/distros/debian/rules b/distros/debian/rules index fe725c2d0..13e36d461 100755 --- a/distros/debian/rules +++ b/distros/debian/rules @@ -21,11 +21,11 @@ override_dh_auto_configure: -DZM_CGIDIR=/usr/lib/zoneminder/cgi-bin \ -DZM_WEB_USER=www-data \ -DZM_WEB_GROUP=www-data \ - -DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \ + -DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \ -DZM_CONFIG_DIR="/etc/zm" \ - -DZM_DIR_EVENTS="/var/cache/zoneminder/events" \ - -DZM_DIR_IMAGES="/var/cache/zoneminder/images" \ - -DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" + -DZM_DIR_EVENTS="/var/cache/zoneminder/events" \ + -DZM_PATH_SHUTDOWN="/sbin/shutdown" \ + -DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" override_dh_auto_install: dh_auto_install --buildsystem=cmake diff --git a/distros/opensuse/CMakeLists.txt b/distros/opensuse/CMakeLists.txt index 10654f26d..6ce1a999f 100644 --- a/distros/opensuse/CMakeLists.txt +++ b/distros/opensuse/CMakeLists.txt @@ -8,16 +8,6 @@ SET(zmgid_final www) SET(webroot /srv/www/htdocs) SET(zm_webdir ${webroot}/zoneminder) -# Download jscalendar & move files into position -file(DOWNLOAD http://downloads.sourceforge.net/jscalendar/jscalendar-1.0.zip ${CMAKE_CURRENT_SOURCE_DIR}/jscalendar-1.0.zip STATUS download_jsc) -if(download_jsc EQUAL 0) -message(STATUS "Jscalander successfully downloaded. Installing...") -execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/jscalendar.sh WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ERROR_VARIABLE unzip_jsc) -message(STATUS "Status of jscalender script was: ${unzip_jsc}") -else(download_jsc EQUAL 0) -message(STATUS "Unable to download optional jscalander. Skipping...") -endif(download_jsc EQUAL 0) - # Create several empty folders file(MAKE_DIRECTORY sock swap zoneminder zoneminder-upload events images temp) @@ -45,7 +35,3 @@ install(FILES zoneminder.tmpfiles DESTINATION /etc/tmpfiles.d RENAME zoneminder. install(FILES redalert.wav DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/sounds PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(FILES zoneminder.service DESTINATION /usr/lib/systemd/system PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) -# Install jscalendar -if(unzip_jsc STREQUAL "") -install(DIRECTORY jscalendar-1.0/ DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/tools/jscalendar) -endif(unzip_jsc STREQUAL "") diff --git a/distros/opensuse/jscalendar.sh b/distros/opensuse/jscalendar.sh deleted file mode 100755 index 80acaafec..000000000 --- a/distros/opensuse/jscalendar.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -unzip -o jscalendar-1.0.zip -mkdir -v jscalendar-doc -cd jscalendar-1.0 -mv -v *html *php doc/* README ../jscalendar-doc -rmdir -v doc diff --git a/distros/opensuse/zoneminder.cmake.OS13.spec b/distros/opensuse/zoneminder.cmake.OS13.spec index e1a55d3cb..e1ed14325 100644 --- a/distros/opensuse/zoneminder.cmake.OS13.spec +++ b/distros/opensuse/zoneminder.cmake.OS13.spec @@ -16,7 +16,6 @@ Version: 1.27.0 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons -# jscalendar is LGPL (any version): http://www.dynarch.com/projects/calendar/ # Mootools is under the MIT license: http://mootools.net/ License: GPLv2+ and LGPLv2+ and MIT URL: http://www.zoneminder.com/ @@ -141,7 +140,7 @@ fi %files %defattr(-,root,root,-) -%doc AUTHORS COPYING README.md distros/opensuse/README.OpenSuse distros/opensuse/jscalendar-doc +%doc AUTHORS COPYING README.md distros/opensuse/README.OpenSuse %docdir /opt/zoneminder/share/man %config %attr(640,root,%{zmgid_final}) /etc/zm.conf %config(noreplace) %attr(644,root,root) /etc/apache2/conf.d/zoneminder.conf diff --git a/distros/redhat/CMakeLists.txt b/distros/redhat/CMakeLists.txt index e42c5db61..f1a1bc75b 100644 --- a/distros/redhat/CMakeLists.txt +++ b/distros/redhat/CMakeLists.txt @@ -1,4 +1,9 @@ -# CMakeLists.txt for the Redhat/CentOS Target Distro. +# CMakeLists.txt for the Redhat Target Distros. + +# +# General strategy is to configure and install all files specific to Apache and Nginx +# Then let the rpm specfile sort them into the appropriate sub-package +# # Display a message to show the RHEL build options are being processed. if(ZM_TARGET_DISTRO MATCHES "^el") @@ -9,46 +14,62 @@ else(ZM_TARGET_DISTRO MATCHES "^el") message([WARNING] "Unknown Build Option Detected" ...) endif(ZM_TARGET_DISTRO MATCHES "^el") -if((NOT ZM_TARGET_DISTRO MATCHES "^fc") AND (ZM_WEB_USER STREQUAL "nginx")) - message([FATAL_ERROR] "Experimental Nginx support is currently only supported on Fedora" ...) -endif((NOT ZM_TARGET_DISTRO MATCHES "^fc") AND (ZM_WEB_USER STREQUAL "nginx")) +# +# CONFIGURE STAGE +# -# Configure the zoneminder service files -configure_file(systemd/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY) -if(ZM_WEB_USER STREQUAL "nginx") - configure_file(nginx/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY) - configure_file(nginx/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY) - configure_file(nginx/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY) - configure_file(nginx/zoneminder.php-fpm.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.php-fpm.conf @ONLY) - configure_file(nginx/README.Fedora ${CMAKE_CURRENT_SOURCE_DIR}/readme/README.Fedora COPYONLY) -else(ZM_WEB_USER STREQUAL "nginx") - configure_file(systemd/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY) - configure_file(apache/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY) - configure_file(systemd/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY) -endif(ZM_WEB_USER STREQUAL "nginx") +# 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) -# Unpack jscalendar & move files into position -message(STATUS "Unpacking and Installing jscalendar...") -execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/misc/jscalendar.sh - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - ERROR_VARIABLE unzip_jsc - ) -if("${unzip_jsc}" STREQUAL "") - message(STATUS "jscalendar successfully installed.") -else("${unzip_jsc}" STREQUAL "") - message(FATAL_ERROR "\nAn error occurred while jscalendar was being processed:\n${unzip_jsc}") -endif("${unzip_jsc}" STREQUAL "") +# Configure the Apache zoneminder files +configure_file(httpd/zm-httpd.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zm-httpd.conf @ONLY) +configure_file(httpd/zoneminder.httpd.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.httpd.conf @ONLY) +configure_file(httpd/zoneminder.httpd.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.httpd.tmpfiles.conf @ONLY) +configure_file(httpd/com.zoneminder.systemctl.rules.httpd.in ${CMAKE_CURRENT_SOURCE_DIR}/com.zoneminder.systemctl.rules.httpd @ONLY) -# Create several empty folders -file(MAKE_DIRECTORY sock swap zoneminder zoneminder-upload events images temp) +# Configure the Nginx zoneminder files +configure_file(nginx/zm-nginx.conf ${CMAKE_CURRENT_SOURCE_DIR}/zm-nginx.conf COPYONLY) +configure_file(nginx/zoneminder.nginx.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.nginx.conf @ONLY) +configure_file(nginx/redirect.nginx.conf ${CMAKE_CURRENT_SOURCE_DIR}/redirect.nginx.conf COPYONLY) +configure_file(nginx/zoneminder.nginx.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.nginx.tmpfiles.conf @ONLY) +configure_file(nginx/zm-web-user.conf ${CMAKE_CURRENT_SOURCE_DIR}/zm-web-user.conf COPYONLY) +configure_file(nginx/zoneminder.php-fpm.conf ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.php-fpm.conf COPYONLY) +configure_file(nginx/com.zoneminder.systemctl.rules.nginx ${CMAKE_CURRENT_SOURCE_DIR}/com.zoneminder.systemctl.rules.nginx COPYONLY) + +# +# INSTALLATION STAGE +# + + +# Install the common zoneminder files +install(FILES zoneminder.logrotate DESTINATION /etc/logrotate.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +install(FILES zoneminder.service DESTINATION /usr/lib/systemd/system PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) -# Install the empty folders install(DIRECTORY sock swap DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) 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 images temp DESTINATION /var/lib/zoneminder 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 +install(FILES zm-httpd.conf DESTINATION /usr/lib/systemd/system/zoneminder.service.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +install(FILES zoneminder.httpd.conf DESTINATION /etc/zm/www PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +install(FILES zoneminder.httpd.tmpfiles.conf DESTINATION /usr/lib/tmpfiles.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +install(FILES com.zoneminder.systemctl.rules.httpd DESTINATION /etc/zm/www PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) + +# Install the Nginx zoneminder files +install(FILES zm-nginx.conf DESTINATION /usr/lib/systemd/system/zoneminder.service.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +install(FILES zoneminder.nginx.conf DESTINATION /etc/zm/www PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +install(FILES redirect.nginx.conf DESTINATION /etc/zm/www PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +install(FILES zoneminder.nginx.tmpfiles.conf DESTINATION /usr/lib/tmpfiles.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +install(FILES com.zoneminder.systemctl.rules.nginx DESTINATION /etc/zm/www PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +install(FILES zm-web-user.conf DESTINATION /etc/zm/conf.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +install(FILES zoneminder.php-fpm.conf DESTINATION /etc/php-fpm.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) + +# Miscellaneous # Symlink the cake php temp folder to the ZoneMinder temp folder install(CODE "execute_process(COMMAND ln -sf ../../../../../../var/lib/zoneminder/temp \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/api/app/tmp\")") @@ -57,17 +78,5 @@ install(CODE "execute_process(COMMAND ln -sf ../../../../../../var/lib/zoneminde install(CODE "execute_process(COMMAND ln -sf ../../java/cambozola.jar \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/cambozola.jar\")") # Install auxiliary files -install(FILES misc/redalert.wav DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/sounds PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) -install(DIRECTORY jscalendar-1.0/ DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/tools/jscalendar) - -# Install zoneminder service files -install(FILES zoneminder.logrotate DESTINATION /etc/logrotate.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) -install(FILES zoneminder.conf DESTINATION /etc/zm/www PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) - -if(ZM_WEB_USER STREQUAL "nginx") - install(FILES zoneminder.php-fpm.conf DESTINATION /etc/php-fpm.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ RENAME zoneminder.conf) -endif(ZM_WEB_USER STREQUAL "nginx") - -install(FILES zoneminder.service DESTINATION /usr/lib/systemd/system PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) -install(FILES zoneminder.tmpfiles DESTINATION /usr/lib/tmpfiles.d RENAME zoneminder.conf PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +install(FILES common/redalert.wav DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/sounds PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) diff --git a/distros/redhat/misc/redalert.wav b/distros/redhat/common/redalert.wav similarity index 100% rename from distros/redhat/misc/redalert.wav rename to distros/redhat/common/redalert.wav diff --git a/distros/redhat/common/zoneminder.logrotate.in b/distros/redhat/common/zoneminder.logrotate.in new file mode 100644 index 000000000..210a84c07 --- /dev/null +++ b/distros/redhat/common/zoneminder.logrotate.in @@ -0,0 +1,12 @@ +@ZM_LOGDIR@/*.log { + missingok + notifempty + sharedscripts + delaycompress + compress + postrotate + @BINDIR@/zmpkg.pl logrot > /dev/null 2>/dev/null || true + endscript + daily + rotate 7 +} diff --git a/distros/redhat/systemd/zoneminder.service.in b/distros/redhat/common/zoneminder.service.in similarity index 76% rename from distros/redhat/systemd/zoneminder.service.in rename to distros/redhat/common/zoneminder.service.in index 68918ab9e..8551a60e2 100644 --- a/distros/redhat/systemd/zoneminder.service.in +++ b/distros/redhat/common/zoneminder.service.in @@ -1,13 +1,12 @@ # ZoneMinder systemd unit file for RedHat distros and clones +# See drop-in folder for additional config directives [Unit] Description=ZoneMinder CCTV recording and security system -After=network.target mariadb.service httpd.service -Requires=mariadb.service httpd.service +After=network.target mariadb.service +Requires=mariadb.service [Service] -User=@WEB_USER@ -Group=@WEB_GROUP@ Type=forking ExecStart=@BINDIR@/zmpkg.pl start ExecReload=@BINDIR@/zmpkg.pl restart diff --git a/distros/redhat/httpd/com.zoneminder.systemctl.rules.httpd.in b/distros/redhat/httpd/com.zoneminder.systemctl.rules.httpd.in new file mode 100644 index 000000000..d101dad69 --- /dev/null +++ b/distros/redhat/httpd/com.zoneminder.systemctl.rules.httpd.in @@ -0,0 +1,7 @@ +polkit.addRule(function(action, subject) { + if (action.id == "com.zoneminder.policykit.pkexec.run-zmsystemctl" && + subject.user != "@WEB_USER@") { + return polkit.Result.NO; + } + +}); diff --git a/distros/redhat/httpd/zm-httpd.conf.in b/distros/redhat/httpd/zm-httpd.conf.in new file mode 100644 index 000000000..1a00d8d9b --- /dev/null +++ b/distros/redhat/httpd/zm-httpd.conf.in @@ -0,0 +1,8 @@ +# Additional config directives for ZoneMinder with Apache web server + +[Unit] +After=httpd.service + +[Service] +User=@WEB_USER@ +Group=@WEB_GROUP@ diff --git a/distros/redhat/apache/zoneminder.conf.in b/distros/redhat/httpd/zoneminder.httpd.conf.in similarity index 100% rename from distros/redhat/apache/zoneminder.conf.in rename to distros/redhat/httpd/zoneminder.httpd.conf.in diff --git a/distros/redhat/systemd/zoneminder.tmpfiles.in b/distros/redhat/httpd/zoneminder.httpd.tmpfiles.in similarity index 79% rename from distros/redhat/systemd/zoneminder.tmpfiles.in rename to distros/redhat/httpd/zoneminder.httpd.tmpfiles.in index 21e6119fe..b403fb24d 100644 --- a/distros/redhat/systemd/zoneminder.tmpfiles.in +++ b/distros/redhat/httpd/zoneminder.httpd.tmpfiles.in @@ -2,4 +2,4 @@ D @ZM_TMPDIR@ 0755 @WEB_USER@ @WEB_GROUP@ D @ZM_SOCKDIR@ 0755 @WEB_USER@ @WEB_GROUP@ D @ZM_CACHEDIR@ 0755 @WEB_USER@ @WEB_GROUP@ d @ZM_DIR_EVENTS@ 0755 @WEB_USER@ @WEB_GROUP@ -D @ZM_DIR_IMAGES@ 0755 @WEB_USER@ @WEB_GROUP@ + diff --git a/distros/redhat/misc/jscalendar-1.0.zip b/distros/redhat/misc/jscalendar-1.0.zip deleted file mode 100644 index f33dc072c..000000000 Binary files a/distros/redhat/misc/jscalendar-1.0.zip and /dev/null differ diff --git a/distros/redhat/misc/jscalendar.sh b/distros/redhat/misc/jscalendar.sh deleted file mode 100755 index 068e47bf9..000000000 --- a/distros/redhat/misc/jscalendar.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -unzip -o misc/jscalendar-1.0.zip -mkdir -v jscalendar-doc -cd jscalendar-1.0 -mv -v *html *php doc/* README ../jscalendar-doc -rmdir -v doc diff --git a/distros/redhat/misc/local_zoneminder.te b/distros/redhat/misc/local_zoneminder.te deleted file mode 100644 index c49505785..000000000 --- a/distros/redhat/misc/local_zoneminder.te +++ /dev/null @@ -1,125 +0,0 @@ -module local_zoneminder 1.2; - -require { - type afs_ka_port_t; - type netsupport_port_t; - type port_t; - type presence_port_t; - type postfix_master_t; - type postfix_qmgr_t; - type postfix_pickup_t; - type httpd_t; - type var_lib_t; - type ionixnetmon_port_t; - type glance_port_t; - type mmcc_port_t; - type postfix_master_t; - type commplex_port_t; - type syslogd_port_t; - type dcc_port_t; - type sip_port_t; - type amqp_port_t; - type condor_port_t; - type afs_fs_port_t; - type nodejs_debug_port_t; - type httpd_var_lib_t; - type websm_port_t; - type afs_pt_port_t; - type postfix_qmgr_t; - type git_port_t; - type ipp_port_t; - type aol_port_t; - type unconfined_t; - type kernel_t; - type init_t; - type auditd_t; - type mysqld_t; - type httpd_log_t; - type syslogd_t; - type httpd_t; - type initrc_state_t; - type initrc_t; - type var_lib_t; - type udev_t; - type mysqld_safe_t; - type sshd_t; - type crond_t; - type getty_t; - type httpd_var_lib_t; - type initrc_var_run_t; - type tmpfs_t; - type dhcpc_t; - type v4l_device_t; - type file_t; - class sock_file { write create unlink }; - class unix_stream_socket { read connectto }; - class lnk_file { write create getattr read lock unlink }; - class dir {search getattr }; - class udp_socket name_bind; - class file { write getattr read lock unlink open }; - class shm { unix_read unix_write associate read write getattr }; - class chr_file getattr; -} - -#============= httpd_t ============== -allow httpd_t auditd_t:dir { search getattr }; -allow httpd_t auditd_t:file { read getattr open }; -allow httpd_t crond_t:dir { search getattr }; -allow httpd_t crond_t:file { read getattr open }; -allow httpd_t dhcpc_t:dir { search getattr }; -allow httpd_t dhcpc_t:file { read getattr open }; -allow httpd_t getty_t:dir { search getattr }; -allow httpd_t getty_t:file { read getattr open }; -allow httpd_t httpd_log_t:file write; -allow httpd_t httpd_var_lib_t:lnk_file { write getattr read lock unlink }; -allow httpd_t init_t:dir { search getattr }; -allow httpd_t init_t:file { read getattr open }; -#!!!! The source type 'httpd_t' can write to a 'file' of the following types: -#squirrelmail_spool_t, mirrormanager_var_run_t, dirsrvadmin_config_t, httpd_lock_t, httpd_tmp_t, dirsrv_config_t, dirsrvadmin_tmp_t, httpd_cache_t, httpd_tmpfs_t, httpd_squirrelmail_t, dirsrv_var_run_t, dirsrv_var_log_t, httpd_var_lib_t, httpd_var_run_t, zarafa_var_lib_t, httpd_prewikka_rw_content_t, httpd_mediawiki_rw_content_t, httpd_squid_rw_content_t, passenger_var_run_t, httpd_smokeping_cgi_rw_content_t, httpd_openshift_rw_content_t, httpd_dirsrvadmin_rw_content_t, httpd_w3c_validator_rw_content_t, httpd_collectd_rw_content_t, cluster_var_lib_t, cluster_var_run_t, httpd_user_rw_content_t, httpd_awstats_rw_content_t, httpdcontent, root_t, httpd_cobbler_rw_content_t, httpd_munin_rw_content_t, cluster_conf_t, httpd_bugzilla_rw_content_t, passenger_tmp_t, httpd_cvs_rw_content_t, httpd_git_rw_content_t, httpd_sys_rw_content_t, httpd_sys_rw_content_t, httpd_nagios_rw_content_t, httpd_apcupsd_cgi_rw_content_t, httpd_nutups_cgi_rw_content_t, httpd_dspam_rw_content_t - -allow httpd_t initrc_state_t:file { read write getattr unlink open }; -allow httpd_t initrc_t:unix_stream_socket connectto; -allow httpd_t initrc_t:shm { unix_read unix_write associate read write getattr }; -allow httpd_t initrc_var_run_t:file { write read lock open }; -allow httpd_t kernel_t:dir { search getattr }; -allow httpd_t kernel_t:file { read getattr open }; -allow httpd_t mysqld_safe_t:dir { search getattr }; -allow httpd_t mysqld_safe_t:file { read getattr open }; -allow httpd_t mysqld_t:dir { search getattr }; -allow httpd_t mysqld_t:file { read getattr open }; -allow httpd_t sshd_t:dir { search getattr }; -allow httpd_t sshd_t:file { read getattr open }; -allow httpd_t syslogd_t:dir { search getattr }; -allow httpd_t syslogd_t:file { read getattr open }; -allow httpd_t tmpfs_t:sock_file write; -allow httpd_t udev_t:dir { search getattr }; -allow httpd_t udev_t:file { read getattr open }; -allow httpd_t unconfined_t:dir { search getattr }; -allow httpd_t unconfined_t:file { read getattr open }; -allow httpd_t var_lib_t:lnk_file { write getattr read lock unlink }; -allow httpd_t var_lib_t:sock_file { write unlink }; -allow httpd_t v4l_device_t:chr_file getattr; -allow httpd_t afs_fs_port_t:udp_socket name_bind; -allow httpd_t afs_ka_port_t:udp_socket name_bind; -allow httpd_t afs_pt_port_t:udp_socket name_bind; -allow httpd_t amqp_port_t:udp_socket name_bind; -allow httpd_t aol_port_t:udp_socket name_bind; -allow httpd_t commplex_port_t:udp_socket name_bind; -allow httpd_t condor_port_t:udp_socket name_bind; -allow httpd_t dcc_port_t:udp_socket name_bind; -allow httpd_t git_port_t:udp_socket name_bind; -allow httpd_t glance_port_t:udp_socket name_bind; -allow httpd_t httpd_var_lib_t:lnk_file create; -allow httpd_t ionixnetmon_port_t:udp_socket name_bind; -allow httpd_t ipp_port_t:udp_socket name_bind; -allow httpd_t mmcc_port_t:udp_socket name_bind; -allow httpd_t netsupport_port_t:udp_socket name_bind; -allow httpd_t nodejs_debug_port_t:udp_socket name_bind; -allow httpd_t port_t:udp_socket name_bind; -allow httpd_t postfix_master_t:dir { search getattr }; -allow httpd_t postfix_master_t:file { read getattr open }; -allow httpd_t postfix_pickup_t:dir { search getattr }; -allow httpd_t postfix_pickup_t:file { read getattr open }; -allow httpd_t postfix_qmgr_t:dir { search getattr }; -allow httpd_t postfix_qmgr_t:file { read getattr open }; -allow httpd_t presence_port_t:udp_socket name_bind; diff --git a/distros/redhat/nginx/README.Fedora b/distros/redhat/nginx/README.Fedora deleted file mode 100644 index 0a5168231..000000000 --- a/distros/redhat/nginx/README.Fedora +++ /dev/null @@ -1,165 +0,0 @@ -What's New -========== - -1. This is an *experimental* build of zoneminder which uses the - nginx web server. - -2. The Apache ScriptAlias has been changed from "/cgi-bin/zm/zms" to - "/cgi-bin-zm/zms". This has been to done to avoid this bug: - https://bugzilla.redhat.com/show_bug.cgi?id=973067 - - IMPORTANT: You must manually inspect the value for PATH_ZMS under Options - and verify it is set to "/cgi-bin-zm/nph-zms". Failure to do so will result - in a broken system. You have been warned. - -3. Due to the active state of the ZoneMinder project, we now recommend granting - ALL permission to the ZoneMinder mysql account. This change must be done - manually before ZoneMinder will run. See the installation steps below. - -4. This package uses the HTTPS protocol by default to access the web portal. - Requests using HTTP will auto-redirect to HTTPS. See README.https for - more information. - -5. This package ships with the new ZoneMinder API enabled. - -New installs -============ - -1. This package supports either community-mysql-server or mariadb-server with - mariadb being the preferred choice. Unless you are already using MariaDB or - Mysql server, you need to ensure that the server is configured to start - during boot and properly secured by running: - - sudo dnf install mariadb-server - sudo systemctl enable mariadb - sudo systemctl start mariadb.service - mysql_secure_installation - -2. Assuming the database is local and using the password for the root account - set during the previous step, you will need to create the ZoneMinder - database and configure a database account for ZoneMinder to use: - - mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql - mysql -uroot -p -e "grant all on zm.* to \ - 'zmuser'@localhost identified by 'zmpass';" - mysqladmin -uroot -p reload - - The database account credentials, zmuser/zmpass, are arbitrary. Set them to - anything that suits your environment. - -3. If you have chosen to change the zoneminder database account credentials to - something other than zmuser/zmpass, you must now edit /etc/zm/zm.conf. - Change ZM_DB_USER and ZM_DB_PASS to the values you created in the previous - step. - - This version of zoneminder no longer requires you to make a similar change - to the credentials in /usr/share/zoneminder/www/api/app/Config/database.php - This now happens dynamically. Do *not* make any changes to this file. - -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. - - If you are not sure of the proper timezone specification to use, look at - http://php.net/date.timezone - -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. - - To immediately disbale SELinux for the current seesion, issue the following - from the command line: - - sudo setenforce 0 - - To permanently disable SELinux, edit /etc/selinux/config and change the - SELINUX line from "enforcing" to "disabled". This change will take - effect after a reboot. - -6. This package comes preconfigured for HTTPS using the default self signed - certificate on your system. We recommend you keep this configuration. - - If this does not meet your needs, then read README.https to - learn about alternatives. - -7. Edit /etc/sysconfig/fcgiwrap and set DAEMON_PROCS to the maximum number of - simulatneous streams the server should support. Generally, a good minimum - value for this equals the total number of cameras you expect to view at the - same time. - -8. Now start the web server: - - sudo systemctl enable nginx - sudo systemctl start nginx - -9. Now start zoneminder: - - sudo systemctl enable zoneminder - sudo systemctl start zoneminder - -10.The Fedora repos have a ZoneMinder package available, but it does not - support ffmpeg or libvlc, which many modern IP cameras require. Most users - will want to prevent the ZoneMinder package in the Fedora repos from - overwriting the ZoneMinder package in zmrepo, during a future dnf update. To - prevent that from happening you must edit /etc/yum.repos.d/fedora.repo - and /etc/yum.repos.d/fedora-updates.repo. Add the line "exclude=zoneminder*" - without the quotes under the [fedora] and [fedora-updates] blocks, - respectively. - -Upgrades -======== - -1. Verify /etc/zm/zm.conf. - - If zm.conf was manually edited before running the upgrade, the installation - may not overwrite it. In this case, it will create the file - /etc/zm/zm.conf.rpmnew. - - For example, this will happen if you are using database account credentials - other than zmuser/zmpass. - - Compare /etc/zm/zm.conf to /etc/zm/zm.conf.rpmnew. Verify that zm.conf - contains any new config settings that may be in zm.conf.rpmnew. - - This version of zoneminder no longer requires you to make a similar change - to the credentials in /usr/share/zoneminder/www/api/app/Config/database.php - This now happens dynamically. Do *not* make any changes to this file. - -2. Verify permissions of the zmuser account. - - Over time, the database account permissions required for normal operation - have increased. Verify the zmuser database account has been granted all - permission to the ZoneMinder database: - - mysql -uroot -p -e "show grants for zmuser@localhost;" - - See step 2 of the Installation section to add missing permissions. - -3. Verify the ZoneMinder Apache configuration file in the folder - /etc/httpd/conf.d. You will have a file called "zoneminder.conf" and there - may also be a file called "zoneminder.conf.rpmnew". If the rpmnew file - exists, inspect it and merge anything new in that file with zoneminder.conf. - Verify the SSL REquirements meet your needs. Read README.https if necessary. - -4. Upgrade the database before starting ZoneMinder. - - Most upgrades can be performed by executing the following command: - - sudo zmupdate.pl - - Recent versions of ZoneMinder don't require any parameters added to the - zmupdate command. However, if ZoneMinder complains, you may need to call - zmupdate in the following manner: - - sudo zmupdate.pl --user=root --pass= --version= - -5. Now restart nginx and php-fpm then start and zoneminder: - - sudo systemctl restart nginx - sudo systemctl restart php-fpm - sudo systemctl start zoneminder - diff --git a/distros/redhat/nginx/com.zoneminder.systemctl.rules.nginx b/distros/redhat/nginx/com.zoneminder.systemctl.rules.nginx new file mode 100644 index 000000000..8eaa78d5c --- /dev/null +++ b/distros/redhat/nginx/com.zoneminder.systemctl.rules.nginx @@ -0,0 +1,7 @@ +polkit.addRule(function(action, subject) { + if (action.id == "com.zoneminder.policykit.pkexec.run-zmsystemctl" && + subject.user != "nginx") { + return polkit.Result.NO; + } + +}); diff --git a/distros/redhat/nginx/redirect.nginx.conf b/distros/redhat/nginx/redirect.nginx.conf new file mode 100644 index 000000000..67ef08124 --- /dev/null +++ b/distros/redhat/nginx/redirect.nginx.conf @@ -0,0 +1,2 @@ +# Auto redirect to https +return 301 https://$host$request_uri; diff --git a/distros/redhat/nginx/zm-nginx.conf b/distros/redhat/nginx/zm-nginx.conf new file mode 100644 index 000000000..5f0319b17 --- /dev/null +++ b/distros/redhat/nginx/zm-nginx.conf @@ -0,0 +1,9 @@ +# Additional config directives for ZoneMinder with Nginx web server + +[Unit] +After=nginx.service php-fpm.service fcgiwrap.service +Requires=php-fpm.service fcgiwrap@nginx.service + +[Service] +User=nginx +Group=nginx diff --git a/distros/redhat/nginx/zm-web-user.conf b/distros/redhat/nginx/zm-web-user.conf new file mode 100644 index 000000000..3146679fd --- /dev/null +++ b/distros/redhat/nginx/zm-web-user.conf @@ -0,0 +1,3 @@ +ZM_WEB_USER=nginx +ZM_WEB_GROUP=nginx + diff --git a/distros/redhat/nginx/zoneminder.conf.in b/distros/redhat/nginx/zoneminder.conf.in deleted file mode 100644 index b8ffd816a..000000000 --- a/distros/redhat/nginx/zoneminder.conf.in +++ /dev/null @@ -1,49 +0,0 @@ -listen 443 ssl; -listen [::]:443 ssl; - -ssl_certificate "/etc/pki/tls/certs/localhost.crt"; -ssl_certificate_key "/etc/pki/tls/private/localhost.key"; -ssl_session_cache shared:SSL:1m; -ssl_session_timeout 10m; -ssl_ciphers PROFILE=SYSTEM; -ssl_prefer_server_ciphers on; - -# Auto-redirect HTTP requests to HTTPS -if ($scheme != "https") { - rewrite ^/?(zm)(.*)$ https://$host/$1$2 permanent; -} - -location /cgi-bin-zm { - gzip off; - alias "@ZM_CGIDIR@"; - - include /etc/nginx/fastcgi_params; - fastcgi_param SCRIPT_FILENAME $request_filename; - fastcgi_pass unix:/run/fcgiwrap.sock; -} - -location /zm { - gzip off; - alias "@ZM_WEBDIR@"; - 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:/run/php-fpm/www.sock; - } - - location ~ \.(jpg|jpeg|gif|png|ico)$ { - access_log off; - expires 33d; - } - - location /zm/api/ { - alias "@ZM_WEBDIR@"; - rewrite ^/zm/api(.+)$ /zm/api/index.php?p=$1 last; - } -} - diff --git a/distros/redhat/nginx/zoneminder.nginx.conf.in b/distros/redhat/nginx/zoneminder.nginx.conf.in new file mode 100644 index 000000000..2976a83b2 --- /dev/null +++ b/distros/redhat/nginx/zoneminder.nginx.conf.in @@ -0,0 +1,57 @@ +server { + listen 443 ssl default_server; + listen [::]:443 ssl default_server; + server_name = localhost $hostname; + + ssl_certificate "/etc/pki/tls/certs/localhost.crt"; + ssl_certificate_key "/etc/pki/tls/private/localhost.key"; + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 10m; + ssl_ciphers PROFILE=SYSTEM; + ssl_prefer_server_ciphers on; + + # Auto redirect to server/zm when no url suffix was given + location = / { + return 301 zm; + } + + location /cgi-bin-zm { + gzip off; + alias "@ZM_CGIDIR@"; + + include /etc/nginx/fastcgi_params; + fastcgi_param SCRIPT_FILENAME $request_filename; + fastcgi_pass unix:/run/fcgiwrap/fcgiwrap-nginx.sock; + } + + location /zm/cache { + alias "@ZM_CACHEDIR@"; + } + + location /zm { + gzip off; + alias "@ZM_WEBDIR@"; + index index.php; + + location ~ \.php$ { + try_files $uri =404; + expires epoch; + include /etc/nginx/fastcgi_params; + fastcgi_param SCRIPT_FILENAME $request_filename; + fastcgi_index index.php; + fastcgi_pass unix:/run/php-fpm/www.sock; + } + + location ~ \.(jpg|jpeg|gif|png|ico)$ { + access_log off; + expires 33d; + } + + location /zm/api/ { + alias "@ZM_WEBDIR@"; + rewrite ^/zm/api(.+)$ /zm/api/app/webroot/index.php?p=$1 last; + } + } + +} + diff --git a/distros/redhat/nginx/zoneminder.nginx.tmpfiles.in b/distros/redhat/nginx/zoneminder.nginx.tmpfiles.in new file mode 100644 index 000000000..502b817d8 --- /dev/null +++ b/distros/redhat/nginx/zoneminder.nginx.tmpfiles.in @@ -0,0 +1,5 @@ +D @ZM_TMPDIR@ 0755 nginx nginx +D @ZM_SOCKDIR@ 0755 nginx nginx +D @ZM_CACHEDIR@ 0755 nginx nginx +d @ZM_DIR_EVENTS@ 0755 nginx nginx + diff --git a/distros/redhat/nginx/zoneminder.php-fpm.conf b/distros/redhat/nginx/zoneminder.php-fpm.conf new file mode 100644 index 000000000..cd60a5120 --- /dev/null +++ b/distros/redhat/nginx/zoneminder.php-fpm.conf @@ -0,0 +1,14 @@ +; This config file is needed when using ZoneMinder with web servers other +; than Apache. You can ignore this file if you are using Apache web server. +; Change the user and group of the default pool to the web server account +[www] + +user = nginx +group = nginx + +; These parameters are typically a tradoff between performance and memory +; consumption. See the contents of www.conf for details. + +pm = ondemand +pm.max_children = 50 +pm.process_idle_timeout = 10s diff --git a/distros/redhat/nginx/zoneminder.php-fpm.conf.in b/distros/redhat/nginx/zoneminder.php-fpm.conf.in deleted file mode 100644 index 26e8c62cf..000000000 --- a/distros/redhat/nginx/zoneminder.php-fpm.conf.in +++ /dev/null @@ -1,10 +0,0 @@ -# Change the user and group of the default pool to the web server account -[www] - -user = @WEB_USER@ -group = @WEB_GROUP@ - -# Uncomment these on machines with little memory -#pm = ondemand -#pm.max_children = 10 -#pm.process_idle_timeout = 10s diff --git a/distros/redhat/nginx/zoneminder.service.in b/distros/redhat/nginx/zoneminder.service.in deleted file mode 100644 index 7e2e36585..000000000 --- a/distros/redhat/nginx/zoneminder.service.in +++ /dev/null @@ -1,22 +0,0 @@ -# ZoneMinder systemd unit file for Fedora -# Replace mariadb with community-mysql if using mysql service instead of mariadb - -[Unit] -Description=ZoneMinder CCTV recording and security system -After=network.target mariadb.service nginx.service php-fpm.service fcgiwrap.service -Requires=mariadb.service nginx.service php-fpm.service fcgiwrap.service - -[Service] -User=@WEB_USER@ -Group=@WEB_GROUP@ -Type=forking -ExecStart=@BINDIR@/zmpkg.pl start -ExecReload=@BINDIR@/zmpkg.pl restart -ExecStop=@BINDIR@/zmpkg.pl stop -PIDFile=@ZM_RUNDIR@/zm.pid -Environment=TZ=/etc/localtime -RuntimeDirectory=zoneminder -RuntimeDirectoryMode=0755 - -[Install] -WantedBy=multi-user.target diff --git a/distros/redhat/nginx/zoneminder.tmpfiles.in b/distros/redhat/nginx/zoneminder.tmpfiles.in deleted file mode 100644 index 8040a7877..000000000 --- a/distros/redhat/nginx/zoneminder.tmpfiles.in +++ /dev/null @@ -1,5 +0,0 @@ -D @ZM_TMPDIR@ 0755 @WEB_USER@ @WEB_GROUP@ -D @ZM_SOCKDIR@ 0755 @WEB_USER@ @WEB_GROUP@ -D /var/lib/php/session 770 root @WEB_GROUP@ -D /var/lib/php/wsdlcache 770 root @WEB_GROUP@ - diff --git a/distros/redhat/readme/README b/distros/redhat/readme/README new file mode 100644 index 000000000..f0fff828f --- /dev/null +++ b/distros/redhat/readme/README @@ -0,0 +1,32 @@ +What's New +========== + +1. See the ZoneMinder release notes for a list of new features: + https://github.com/ZoneMinder/zoneminder/releases + +2. The contents of the ZoneMinder Apache config file have changed. In + addition, this ZoneMinder package now requires you to manually symlink the + ZoneMinder Apache config file. See new install step 6 and upgrade step 3 + in the appropriate README for details. + +3. This package has been split into sub-packages to allow compatibility with + other web servers. Here is a breakdown of the available packages: + + zoneminder - Meta-package installs zoneminder-common and zoneminder-httpd + This exists soley for backwards compatibility. + zoneminder-common - Common files that do not differ based on the web server + zoneminder-httpd - Files needed for compatibility with the Apache web server + zoneminder-nginx - Files needed for compatibility with the Nginx web server + + You can switch between different subpackages with dnf/yum. Be advised that, + switching between httpd <-> nginx requires manaully changing ownership of + all event folders and the php session folder after the change. + +4. The timezone must now be set from the ZoneMinder web console. See the + appropriate README, mentioned in the next step, for details. + +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.Fedora b/distros/redhat/readme/README.httpd similarity index 77% rename from distros/redhat/readme/README.Fedora rename to distros/redhat/readme/README.httpd index 3366c31a6..1ba0ee688 100644 --- a/distros/redhat/readme/README.Fedora +++ b/distros/redhat/readme/README.httpd @@ -1,17 +1,8 @@ -What's New -========== - -1. See the ZoneMinder release notes for a list of new features: - https://github.com/ZoneMinder/zoneminder/releases - -2. The contents of the ZoneMinder Apache config file have changed. In - addition, this ZoneMinder package now requires you to manually symlink the - ZoneMinder Apache config file. See new install step 6 and upgrade step 3 - below for details. - New installs ============ +NOTE: EL7 users should replace "dnf" with "yum" in the instructions below. + 1. Unless you are already using MariaDB server, you need to ensure that the server is configured to start during boot and properly secured by running: @@ -45,20 +36,17 @@ New installs 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: @@ -72,19 +60,19 @@ New installs 6. Configure the web server This package uses the HTTPS protocol by default to access the web portal, - using rhe default self signed certificate on your system. Requests using + using the default self signed certificate on your system. Requests using HTTP will auto-redirect to HTTPS. Inspect the web server configuration file and verify it meets your needs: - /etc/zm/www/zoneminder.conf + /etc/zm/www/zoneminder.httpd.conf If you are running other web enabled services then you may need to edit this file to suite. See README.https to learn about other alternatives. When in doubt, proceed with the default: - sudo ln -s /etc/zm/www/zoneminder.conf /etc/httpd/conf.d/ + sudo ln -sf /etc/zm/www/zoneminder.httpd.conf /etc/httpd/conf.d/ sudo dnf install mod_ssl 7. Now start the web server: @@ -129,7 +117,7 @@ New installs Upgrades ======== -1. Conf.d folder support has been added to ZoneMinder 1.31.0. Any custom +1. Conf.d folder support has been added to ZoneMinder. Any custom changes previously made to zm.conf must now be made in one or more custom config files, created under the conf.d folder. Do this now. See /etc/zm/conf.d/README for details. Once you recreate any custom config changes @@ -146,10 +134,17 @@ Upgrades See step 2 of the Installation section to add missing permissions. 3. Verify the ZoneMinder Apache configuration file in the folder - /etc/zm/www. You will have a file called "zoneminder.conf" and there - may also be a file called "zoneminder.conf.rpmnew". If the rpmnew file + /etc/zm/www. You will have a file called "zoneminder.httpd.conf" and there + may also be one or more files with "rpmnew" extenstion. If the rpmnew file exists, inspect it and merge anything new in that file with zoneminder.conf. - Verify the SSL REquirements meet your needs. Read README.https if necessary. + Verify the SSL Requirements meet your needs. Read README.https if necessary. + + The contents of this file must be merged into your Apache configuration. + See step 6 of the installation section if you have not already done this + during a previous upgrade. + + IMPORTANT: Failure to complete this step properly will result in a mostly + empty or significantly corrupted web console post-upgrade. 4. Upgrade the database before starting ZoneMinder. @@ -168,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.https b/distros/redhat/readme/README.https index 4b02aaa0d..620588bf9 100644 --- a/distros/redhat/readme/README.https +++ b/distros/redhat/readme/README.https @@ -20,7 +20,8 @@ experience. to do this: https://wiki.centos.org/HowTos/Https . Additionally, Googling "centos certificate" reveals many articles on the subject. -3. You can turn off HTTPS entirely by simply commenting out the SSLRequireSSL - directives found in /etc/httpd/conf.d/zoneminder.conf. You should also - comment out the HTTP -> HTTPS Rewrite rule. +3. When using Apache, you can turn off HTTPS entirely by simply commenting + out the SSLRequireSSL directives found in + /etc/zm/www/zoneminder.apache.conf. You should also comment out the + HTTP -> HTTPS Rewrite rule. diff --git a/distros/redhat/readme/README.Redhat7 b/distros/redhat/readme/README.nginx similarity index 69% rename from distros/redhat/readme/README.Redhat7 rename to distros/redhat/readme/README.nginx index bb2dcdaa0..8bc3bbdc1 100644 --- a/distros/redhat/readme/README.Redhat7 +++ b/distros/redhat/readme/README.nginx @@ -1,28 +1,17 @@ -What's New -========== - -1. See the ZoneMinder release notes for a list of new features: - https://github.com/ZoneMinder/zoneminder/releases - -2. The contents of the ZoneMinder Apache config file have changed. In - addition, this ZoneMinder package now requires you to manually symlink the - ZoneMinder Apache config file. See new install step 6 and upgrade step 3 - below for details. - New installs ============ 1. Unless you are already using MariaDB server, you need to ensure that the server is configured to start during boot and properly secured by running: - sudo yum install mariadb-server + sudo dnf install mariadb-server sudo systemctl enable mariadb sudo systemctl start mariadb.service mysql_secure_installation -2. Using the password for the root account set during the previous step, you - will need to create the ZoneMinder database and configure a database - account for ZoneMinder to use: +2. Assuming the database is local and using the password for the root account + set during the previous step, you will need to create the ZoneMinder + database and configure a database account for ZoneMinder to use: mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql mysql -uroot -p -e "grant all on zm.* to \ @@ -42,21 +31,21 @@ New installs Once the file has been saved, set proper file & ownership permissions on it: - sudo chown root:apache *.conf - sudo chmod 640 *.conf + 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 We currently do not have the resources to create and maintain an accurate - SELinux policy for ZoneMinder on CentOS 7. We will gladly accept pull + 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. @@ -72,8 +61,7 @@ New installs 6. Configure the web server This package uses the HTTPS protocol by default to access the web portal, - using rhe default self signed certificate on your system. Requests using - HTTP will auto-redirect to HTTPS. + using the default self signed certificate on your system. Inspect the web server configuration file and verify it meets your needs: @@ -82,22 +70,30 @@ New installs If you are running other web enabled services then you may need to edit this file to suite. See README.https to learn about other alternatives. + If you wish http requests to auto-redirect to https requests, then link or + copy /etc/zm/www/redirect.nginx.conf into /etc/nginx/default.d folder. + When in doubt, proceed with the default: - sudo ln -s /etc/zm/www/zoneminder.conf /etc/httpd/conf.d/ - sudo yum install mod_ssl + sudo ln -sf /etc/zm/www/zoneminder.nginx.conf /etc/nginx/conf.d/ + sudo ln -sf /etc/zm/www/redirect.nginx.conf /etc/nginx/default.d/ -7. Now start the web server: +7. Edit /etc/sysconfig/fcgiwrap and set DAEMON_PROCS to the maximum number of + simulatneous streams the server should support. Generally, a good minimum + value for this equals the total number of cameras you expect to view at the + same time. - sudo systemctl enable httpd - sudo systemctl start httpd +8. Now start the web server: -8. Now start zoneminder: + sudo systemctl enable nginx + sudo systemctl start nginx + +9. Now start zoneminder: sudo systemctl enable zoneminder sudo systemctl start zoneminder -9. Optionally configure the firewall +10. Optionally configure the firewall All Redhat distros ship with the firewall enabled. That means you will not be able to access the ZoneMinder web console from a remote machine until @@ -117,7 +113,7 @@ New installs security requirements and how you use the system. It is up to you to verify these commands are sufficient. -10. Access the ZoneMinder web console +11. Access the ZoneMinder web console You may now access the ZoneMinder web console from your web browser using an appropriate url. Here are some examples: @@ -129,7 +125,7 @@ New installs Upgrades ======== -1. Conf.d folder support has been added to ZoneMinder 1.31.0. Any custom +1. Conf.d folder support has been added to ZoneMinder. Any custom changes previously made to zm.conf must now be made in one or more custom config files, created under the conf.d folder. Do this now. See /etc/zm/conf.d/README for details. Once you recreate any custom config changes @@ -144,13 +140,17 @@ Upgrades mysql -uroot -p -e "show grants for zmuser@localhost;" See step 2 of the Installation section to add missing permissions. - -3. Verify the ZoneMinder Apache configuration file in the folder + +3. Verify the ZoneMinder Nginx configuration file in the folder /etc/zm/www. You will have a file called "zoneminder.conf" and there may also be a file called "zoneminder.conf.rpmnew". If the rpmnew file exists, inspect it and merge anything new in that file with zoneminder.conf. Verify the SSL REquirements meet your needs. Read README.https if necessary. + The contents of this file must be merged into your Nginx configuration. + See step 6 of the installation section if you have not already done this + during a previous upgrade. + 4. Upgrade the database before starting ZoneMinder. Most upgrades can be performed by executing the following command: @@ -163,9 +163,17 @@ Upgrades sudo zmupdate.pl --user=root --pass= --version= -5. Now restart the web server then start zoneminder: +5. Now restart nginx and php-fpm then start zoneminder: - sudo systemctl restart httpd + sudo systemctl restart nginx + 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/systemd/zoneminder.logrotate.in b/distros/redhat/systemd/zoneminder.logrotate.in deleted file mode 100644 index b4919eb5e..000000000 --- a/distros/redhat/systemd/zoneminder.logrotate.in +++ /dev/null @@ -1,8 +0,0 @@ -@ZM_LOGDIR@/*.log { - missingok - notifempty - sharedscripts - postrotate - @BINDIR@/zmpkg.pl logrot 2> /dev/null > /dev/null || : - endscript -} diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 644bd5ee7..7297648a9 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -1,3 +1,4 @@ +# Leaving this to allow one to build zoneminder-http subpackage using arbitrary user account %global zmuid_final apache %global zmgid_final apache @@ -7,30 +8,30 @@ # CakePHP-Enum-Behavior is configured as a git submodule %global ceb_version 1.0-zm -%if "%{zmuid_final}" == "nginx" -%global with_nginx 1 -%endif - %global sslcert %{_sysconfdir}/pki/tls/certs/localhost.crt %global sslkey %{_sysconfdir}/pki/tls/private/localhost.key # 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 -%global readme_suffix %{?rhel:Redhat%{?rhel}}%{!?rhel:Fedora} +# 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.31.44 +Version: 1.34.3 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons -# jscalendar is LGPL (any version): http://www.dynarch.com/projects/calendar/ # Mootools is inder the MIT license: http://mootools.net/ # CakePHP is under the MIT license: https://github.com/cakephp/cakephp # Crud is under the MIT license: https://github.com/FriendsOfCake/crud @@ -46,13 +47,14 @@ BuildRequires: systemd-devel BuildRequires: mariadb-devel BuildRequires: perl-podlators BuildRequires: polkit-devel -BuildRequires: cmake >= 2.8.7 +BuildRequires: cmake3 BuildRequires: gnutls-devel BuildRequires: bzip2-devel BuildRequires: pcre-devel BuildRequires: libjpeg-turbo-devel BuildRequires: findutils BuildRequires: coreutils +BuildRequires: net-tools BuildRequires: perl BuildRequires: perl-generators BuildRequires: perl(Archive::Tar) @@ -75,6 +77,8 @@ BuildRequires: vlc-devel BuildRequires: libcurl-devel BuildRequires: libv4l-devel BuildRequires: desktop-file-utils +BuildRequires: gzip +BuildRequires: zlib-devel # ZoneMinder looks for and records the location of the ffmpeg binary during build BuildRequires: ffmpeg @@ -84,15 +88,29 @@ BuildRequires: ffmpeg-devel BuildRequires: libmp4v2-devel BuildRequires: x264-devel -%{?with_nginx:Requires: nginx} -%{?with_nginx:Requires: fcgiwrap} -%{?with_nginx:Requires: php-fpm} -%{!?with_nginx:Requires: httpd} -%{!?with_nginx:Requires: php} +# Allow existing user base to seamlessly transition to sub-packages +Requires: %{name}-common%{?_isa} = %{version}-%{release} +Requires: %{name}-httpd%{?_isa} = %{version}-%{release} + +%description +ZoneMinder is a set of applications which is intended to provide a complete +solution allowing you to capture, analyze, record and monitor any cameras you +have attached to a Linux based machine. It is designed to run on kernels which +support the Video For Linux (V4L) interface and has been tested with cameras +attached to BTTV cards, various USB cameras and IP network cameras. It is +designed to support as many cameras as you can attach to your computer without +too much degradation of performance. + +This is a meta package for backwards compatibility with the existing +ZoneMinder user base. + +%package common +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 @@ -113,16 +131,12 @@ Requires: perl(Net::FTP) Requires: perl(LWP::Protocol::https) Requires: ca-certificates Requires: zip - -Requires(post): systemd -Requires(post): systemd-sysv -Requires(preun): systemd -Requires(postun): systemd +%{?systemd_requires} Requires(post): %{_bindir}/gpasswd -Requires(post): %{_bindir}/less +Requires(post): %{_bindir}/chown -%description +%description common ZoneMinder is a set of applications which is intended to provide a complete solution allowing you to capture, analyze, record and monitor any cameras you have attached to a Linux based machine. It is designed to run on kernels which @@ -131,15 +145,57 @@ attached to BTTV cards, various USB cameras and IP network cameras. It is designed to support as many cameras as you can attach to your computer without too much degradation of performance. +This is a meta-package that exists solely to allow the existing user base to +seamlessly transition to sub-packages. + +%package httpd +Summary: ZoneMinder configuration for Apache web server +Requires: %{name}-common%{?_isa} = %{version}-%{release} +Requires: httpd +Requires: php + +Conflicts: %{name}-nginx + +%description httpd +ZoneMinder is a set of applications which is intended to provide a complete +solution allowing you to capture, analyze, record and monitor any cameras you +have attached to a Linux based machine. It is designed to run on kernels which +support the Video For Linux (V4L) interface and has been tested with cameras +attached to BTTV cards, various USB cameras and IP network cameras. It is +designed to support as many cameras as you can attach to your computer without +too much degradation of performance. + +This sub-package contains configuration specific to Apache web server + +%package nginx +Summary: ZoneMinder configuration for Nginx web server +Requires: %{name}-common%{?_isa} = %{version}-%{release} +Requires: nginx +Requires: php-fpm +Requires: fcgiwrap + +Conflicts: %{name}-httpd + +%description nginx +ZoneMinder is a set of applications which is intended to provide a complete +solution allowing you to capture, analyze, record and monitor any cameras you +have attached to a Linux based machine. It is designed to run on kernels which +support the Video For Linux (V4L) interface and has been tested with cameras +attached to BTTV cards, various USB cameras and IP network cameras. It is +designed to support as many cameras as you can attach to your computer without +too much degradation of performance. + +This sub-package contains support for ZoneMinder with the Nginx web server + %prep -%autosetup -p 1 -a 1 -n ZoneMinder-%{version} -%{__rm} -rf ./web/api/app/Plugin/Crud -%{__mv} -f crud-%{crud_version} ./web/api/app/Plugin/Crud +%autosetup -p 1 -a 1 +rm -rf ./web/api/app/Plugin/Crud +mv -f crud-%{crud_version} ./web/api/app/Plugin/Crud # The all powerful autosetup macro does not work after the second source tarball -%{__gzip} -dc %{_sourcedir}/cakephp-enum-behavior-%{ceb_version}.tar.gz | tar -xvvf - -%{__rm} -rf ./web/api/app/Plugin/CakePHP-Enum-Behavior -%{__mv} -f CakePHP-Enum-Behavior-%{ceb_version} ./web/api/app/Plugin/CakePHP-Enum-Behavior +gzip -dc %{_sourcedir}/cakephp-enum-behavior-%{ceb_version}.tar.gz | tar -xvvf - +rm -rf ./web/api/app/Plugin/CakePHP-Enum-Behavior +mv -f CakePHP-Enum-Behavior-%{ceb_version} ./web/api/app/Plugin/CakePHP-Enum-Behavior # Change the following default values ./utils/zmeditconfigdata.sh ZM_OPT_CAMBOZOLA yes @@ -150,9 +206,9 @@ too much degradation of performance. ./utils/zmeditconfigdata.sh ZM_OPT_FAST_DELETE no %build -%cmake \ +%cmake3 \ -DZM_WEB_USER="%{zmuid_final}" \ - -DZM_WEB_GROUP="%{zmuid_final}" \ + -DZM_WEB_GROUP="%{zmgid_final}" \ -DZM_TARGET_DISTRO="%{zmtargetdistro}" \ . @@ -174,10 +230,13 @@ find %{buildroot} \( -name .htaccess -or -name .editorconfig -or -name .packlist find %{buildroot}%{_datadir}/zoneminder/www/api \( -name cake -or -name cake.php \) -type f -exec sed -i 's\^#!/usr/bin/env bash$\#!%{_buildshell}\' {} \; -exec %{__chmod} 755 {} \; # Use the system cacert file rather then the one bundled with CakePHP -%{__rm} -f %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem -%{__ln_s} ../../../../../../../..%{_sysconfdir}/pki/tls/certs/ca-bundle.crt %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem +rm -f %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem +ln -s ../../../../../../../..%{_sysconfdir}/pki/tls/certs/ca-bundle.crt %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem -%post +# Handle the polkit file differently for web server agnostic support (see post) +rm -f %{buildroot}%{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules + +%post common # Initial installation if [ $1 -eq 1 ] ; then %systemd_post %{name}.service @@ -185,28 +244,48 @@ fi # Upgrade from a previous version of zoneminder if [ $1 -eq 2 ] ; then - # Add any new PTZ control configurations to the database (will not overwrite) %{_bindir}/zmcamtool.pl --import >/dev/null 2>&1 || : # Freshen the database %{_bindir}/zmupdate.pl -f >/dev/null 2>&1 || : - - # We can't run this automatically when new sql account permissions need to - # be manually added first - # Run zmupdate non-interactively - # zmupdate.pl --nointeractive fi +# Warn the end user to read the README file +echo -e "\nVERY IMPORTANT: Before starting ZoneMinder, you must read the README file\nto finish the installation or upgrade!" +echo -e "\nThe README file is located here: %{_pkgdocdir}-common/README\n" + +%post httpd +# For the case of changing from nginx <-> httpd, files in these folders must change ownership if they exist +%{_bindir}/chown -R %{zmuid_final}:%{zmgid_final} %{_sharedstatedir}/php/session/* >/dev/null 2>&1 || : +%{_bindir}/chown -R %{zmuid_final}:%{zmgid_final} %{_localstatedir}/log/zoneminder/* >/dev/null 2>&1 || : + +ln -sf %{_sysconfdir}/zm/www/com.zoneminder.systemctl.rules.httpd %{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules +# backwards compatibility +ln -sf %{_sysconfdir}/zm/www/zoneminder.httpd.conf %{_sysconfdir}/zm/www/zoneminder.conf + # Allow zoneminder access to local video sources, serial ports, and x10 %{_bindir}/gpasswd -a %{zmuid_final} video >/dev/null 2>&1 || : %{_bindir}/gpasswd -a %{zmuid_final} dialout >/dev/null 2>&1 || : -# Warn the end user to read the README file -echo -e "\nVERY IMPORTANT: Before starting ZoneMinder, read README.%{readme_suffix} to finish the\ninstallation or upgrade!\n" -echo -e "\nThe README file is located here: %{_docdir}/%{name}\n" +%post nginx + +# Php package owns the session folder and sets group ownership to apache account +# We could override the folder permission, but adding nginx to the apache group works better +%{_bindir}/gpasswd -a nginx apache >/dev/null 2>&1 || : + +# For the case of changing from httpd <-> nginx, files in these folders must change ownership if they exist +%{_bindir}/chown -R nginx:nginx %{_sharedstatedir}/php/session/* >/dev/null 2>&1 || : +%{_bindir}/chown -R nginx:nginx %{_localstatedir}/log/zoneminder/* >/dev/null 2>&1 || : + +ln -sf %{_sysconfdir}/zm/www/com.zoneminder.systemctl.rules.nginx %{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules +# backwards compatibility +ln -sf %{_sysconfdir}/zm/www/zoneminder.nginx.conf %{_sysconfdir}/zm/www/zoneminder.conf + +# Allow zoneminder access to local video sources, serial ports, and x10 +%{_bindir}/gpasswd -a nginx video >/dev/null 2>&1 || : +%{_bindir}/gpasswd -a nginx dialout >/dev/null 2>&1 || : -%if 0%{?with_nginx} # Nginx does not create an SSL certificate like the apache package does so lets do that here if [ -f %{sslkey} -o -f %{sslcert} ]; then exit 0 @@ -232,7 +311,6 @@ SomeOrganizationalUnit ${FQDN} root@${FQDN} EOF -%endif %preun %systemd_preun %{name}.service @@ -240,19 +318,12 @@ EOF %postun %systemd_postun_with_restart %{name}.service -%triggerun -- zoneminder < 1.25.0-4 -# Save the current service runlevel info -# User must manually run systemd-sysv-convert --apply zoneminder -# to migrate them to systemd targets -%{_bindir}/systemd-sysv-convert --save zoneminder >/dev/null 2>&1 ||: - -# Run these because the SysV package being removed won't do them -/sbin/chkconfig --del zoneminder >/dev/null 2>&1 || : -/bin/systemctl try-restart zoneminder.service >/dev/null 2>&1 || : - %files +# nothing + +%files common %license COPYING -%doc AUTHORS README.md distros/redhat/readme/README.%{readme_suffix} distros/redhat/readme/README.https distros/redhat/jscalendar-doc +%doc README.md distros/redhat/readme/README distros/redhat/readme/README.httpd distros/redhat/readme/README.nginx distros/redhat/readme/README.https # We want these two folders to have "normal" read permission # compared to the folder contents @@ -262,21 +333,11 @@ EOF # Config folder contents contain sensitive info # and should not be readable by normal users %{_sysconfdir}/zm/conf.d/README -%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/zm.conf -%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/*.conf -%ghost %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/zmcustom.conf -%config(noreplace) %attr(644,root,root) /etc/zm/www/zoneminder.conf %config(noreplace) %{_sysconfdir}/logrotate.d/zoneminder -%if 0%{?with_nginx} -%config(noreplace) %{_sysconfdir}/php-fpm.d/zoneminder.conf -%endif - -%{_tmpfilesdir}/zoneminder.conf %{_unitdir}/zoneminder.service %{_datadir}/polkit-1/actions/com.zoneminder.systemctl.policy -%{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules %{_bindir}/zmsystemctl.pl %{_bindir}/zma @@ -297,6 +358,7 @@ EOF %{_bindir}/zmx10.pl %{_bindir}/zmonvif-probe.pl %{_bindir}/zmstats.pl +%{_bindir}/zmrecover.pl %{perl_vendorlib}/ZoneMinder* %{perl_vendorlib}/ONVIF* @@ -307,25 +369,119 @@ EOF %{_libexecdir}/zoneminder/ %{_datadir}/zoneminder/ -%{_datadir}/applications/*%{name}.desktop +%{_datadir}/applications/*zoneminder.desktop +%files httpd +%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/zm.conf +%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/0*.conf +%ghost %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/zmcustom.conf +%config(noreplace) %{_sysconfdir}/zm/www/zoneminder.httpd.conf +%ghost %{_sysconfdir}/zm/www/zoneminder.conf +%config(noreplace) %{_sysconfdir}/zm/www/com.zoneminder.systemctl.rules.httpd +%ghost %{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules + +%{_unitdir}/zoneminder.service.d/zm-httpd.conf +%{_tmpfilesdir}/zoneminder.httpd.tmpfiles.conf %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/events -%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/images %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/sock %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/swap %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 -%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/run/zoneminder + +%files nginx +%config(noreplace) %attr(640,root,nginx) %{_sysconfdir}/zm/zm.conf +%config(noreplace) %attr(640,root,nginx) %{_sysconfdir}/zm/conf.d/*.conf +%ghost %attr(640,root,nginx) %{_sysconfdir}/zm/conf.d/zmcustom.conf +%config(noreplace) %{_sysconfdir}/zm/www/zoneminder.nginx.conf +%config(noreplace) %{_sysconfdir}/zm/www/redirect.nginx.conf +%ghost %{_sysconfdir}/zm/www/zoneminder.conf +%config(noreplace) %{_sysconfdir}/zm/www/com.zoneminder.systemctl.rules.nginx +%ghost %{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules + +%config(noreplace) %{_sysconfdir}/php-fpm.d/zoneminder.php-fpm.conf + + +%{_unitdir}/zoneminder.service.d/zm-nginx.conf +%{_tmpfilesdir}/zoneminder.nginx.tmpfiles.conf +%dir %attr(755,nginx,nginx) %{_sharedstatedir}/zoneminder +%dir %attr(755,nginx,nginx) %{_sharedstatedir}/zoneminder/events +%dir %attr(755,nginx,nginx) %{_sharedstatedir}/zoneminder/sock +%dir %attr(755,nginx,nginx) %{_sharedstatedir}/zoneminder/swap +%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 Apr 22 2018 Andrew Bauer - 1.31.42-1 -- Remove support for sysvinit a.k.a. el6 -- use desktop-file-install for new zoneminder.desktop file -- add new web cache folder -- 1.31.42 development snapshot +* Tue Feb 04 2020 Andrew Bauer - 1.34.2-1 +- 1.34.2 Release + +* Fri Jan 31 2020 Andrew Bauer - 1.34.1-1 +- 1.34.1 Release + +* Sat Jan 18 2020 Andrew Bauer - 1.34.0-1 +- 1.34.0 Release + +* Tue Dec 17 2019 Leigh Scott - 1.32.3-5 +- Mass rebuild for x264 + +* Wed Aug 07 2019 Leigh Scott - 1.32.3-4 +- Rebuild for new ffmpeg version + +* Tue Mar 12 2019 Sérgio Basto - 1.32.3-3 +- Mass rebuild for x264 + +* 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 +- Break into sub-packages + +* Tue Nov 13 2018 Antonio Trande - 1.32.2-2 +- Rebuild for ffmpeg-3.4.5 on el7 +- Use CMake3 + +* Sat Oct 13 2018 Andrew Bauer - 1.32.2-1 +- 1.32.2 release +- Bug fix release + +* Thu Oct 04 2018 Sérgio Basto - 1.32.1-2 +- Mass rebuild for x264 and/or x265 + +* Tue Oct 2 2018 Andrew Bauer - 1.32.1-1 +- 1.32.1 release +- Bug fix release + +* Wed Sep 12 2018 Andrew Bauer - 1.32.0-1 +- 1.32.0 release +- remove el6 (sys v init) support +- Make README name consistent across all supported distros +- remove jscalendar +- add requires php-json, zip +- support zm/conf.d folder +- support zm cache (busting) folder + +* Sun Aug 19 2018 Leigh Scott - 1.30.4-9 +- Rebuilt for Fedora 29 Mass Rebuild binutils issue + +* Fri Jul 27 2018 RPM Fusion Release Engineering - 1.30.4-8 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Thu Mar 08 2018 RPM Fusion Release Engineering - 1.30.4-7 +- Rebuilt for new ffmpeg snapshot + +* Thu Mar 01 2018 RPM Fusion Release Engineering - 1.30.4-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Thu Jan 18 2018 Leigh Scott - 1.30.4-5 +- Rebuilt for ffmpeg-3.5 git + +* Thu Aug 31 2017 RPM Fusion Release Engineering - 1.30.4-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild * Tue May 09 2017 Andrew Bauer - 1.30.4-1 - modify autosetup macro parameters diff --git a/distros/ubuntu1204/control b/distros/ubuntu1204/control index febcb9435..e5688a421 100644 --- a/distros/ubuntu1204/control +++ b/distros/ubuntu1204/control @@ -1,12 +1,13 @@ Source: zoneminder Section: net Priority: optional -Maintainer: Dmitry Smirnov -Uploaders: Vagrant Cascadian +Maintainer: Isaac Connor +Uploaders: Isaac Connor Build-Depends: debhelper (>= 9), python-sphinx | python3-sphinx, apache2-dev, dh-linktree ,cmake ,libx264-dev, libmp4v2-dev ,libavcodec-dev, libavformat-dev (>= 3:0.svn20090204), libswscale-dev (>= 3:0.svn20090204), libavutil-dev, libavdevice-dev + ,libavresample-dev ,libbz2-dev ,libgcrypt-dev ,libcurl4-gnutls-dev @@ -23,6 +24,9 @@ Build-Depends: debhelper (>= 9), python-sphinx | python3-sphinx, apache2-dev, dh ,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 @@ -49,10 +53,11 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,libdbd-mysql-perl ,libdevice-serialport-perl ,libimage-info-perl - ,libjson-any-perl + ,libjson-maybexs-perl ,libsys-mmap-perl [!hurd-any] ,liburi-encode-perl ,libwww-perl + ,libdatetime-perl ,libdata-uuid-perl ,libnumber-bytes-human-perl ,libfile-slurp-perl @@ -62,8 +67,12 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,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 + ,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 + ,libcrypt-eksblowfish-perl + ,libdata-entropy-perl Recommends: ${misc:Recommends} ,libapache2-mod-php5 | php5-fpm ,mysql-server | virtual-mysql-server @@ -90,7 +99,7 @@ Description: video camera security and surveillance solution # ,libdbd-mysql-perl # ,libdevice-serialport-perl # ,libimage-info-perl -# ,libjson-any-perl +# ,libjson-maybexs-perl # ,libsys-mmap-perl [!hurd-any] # ,liburi-encode-perl # ,libwww-perl diff --git a/distros/ubuntu1204/rules b/distros/ubuntu1204/rules index aed63c110..657697fcf 100755 --- a/distros/ubuntu1204/rules +++ b/distros/ubuntu1204/rules @@ -27,8 +27,8 @@ override_dh_auto_configure: -DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \ -DZM_CACHEDIR="/var/cache/zoneminder/cache" \ -DZM_DIR_EVENTS="/var/cache/zoneminder/events" \ - -DZM_DIR_IMAGES="/var/cache/zoneminder/images" \ - -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) diff --git a/distros/ubuntu1204/zoneminder.logrotate b/distros/ubuntu1204/zoneminder.logrotate index 846abd4fb..3195d0fb2 100644 --- a/distros/ubuntu1204/zoneminder.logrotate +++ b/distros/ubuntu1204/zoneminder.logrotate @@ -9,4 +9,5 @@ endscript daily rotate 7 + maxage 7 } diff --git a/distros/ubuntu1204/zoneminder.postinst b/distros/ubuntu1204/zoneminder.postinst index ef715375b..603786ff6 100644 --- a/distros/ubuntu1204/zoneminder.postinst +++ b/distros/ubuntu1204/zoneminder.postinst @@ -34,6 +34,10 @@ if [ "$1" = "configure" ]; then # test if database if already present... if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf + if [ $? -ne 0 ]; then + echo "Error creating db." + exit 1; + fi # This creates the user. echo "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 diff --git a/distros/ubuntu1410/control b/distros/ubuntu1410/control index 4f979f8c2..5a873f133 100644 --- a/distros/ubuntu1410/control +++ b/distros/ubuntu1410/control @@ -32,7 +32,7 @@ Package: libzoneminder-perl Section: perl Architecture: all Depends: ${misc:Depends}, ${perl:Depends}, libdbi-perl, - libdevice-serialport-perl, libimage-info-perl, libjson-any-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. diff --git a/distros/ubuntu1504_cmake_split_packages/control b/distros/ubuntu1504_cmake_split_packages/control index 5f313897f..b24d67cf2 100644 --- a/distros/ubuntu1504_cmake_split_packages/control +++ b/distros/ubuntu1504_cmake_split_packages/control @@ -45,7 +45,7 @@ Package: libzoneminder-perl Section: perl Architecture: all Depends: ${misc:Depends}, ${perl:Depends}, libdbi-perl, - libdevice-serialport-perl, libimage-info-perl, libjson-any-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. diff --git a/distros/ubuntu1504_cmake_split_packages/rules b/distros/ubuntu1504_cmake_split_packages/rules index 0eca4b511..f6169c495 100755 --- a/distros/ubuntu1504_cmake_split_packages/rules +++ b/distros/ubuntu1504_cmake_split_packages/rules @@ -64,7 +64,6 @@ override_dh_auto_configure: -DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \ -DZM_CONFIG_DIR="/etc/zm" \ -DZM_DIR_EVENTS="/var/cache/zoneminder/events" \ - -DZM_DIR_IMAGES="/var/cache/zoneminder/images" \ -DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" override_dh_auto_test: 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 0b934b7d2..617b3e852 100644 --- a/distros/ubuntu1604/control +++ b/distros/ubuntu1604/control @@ -1,16 +1,19 @@ Source: zoneminder Section: net Priority: optional -Maintainer: Dmitry Smirnov -Uploaders: Vagrant Cascadian -Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apache2-dev, dh-linktree +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 ,libavdevice-dev (>= 6:10~) ,libavcodec-dev (>= 6:10~) ,libavformat-dev (>= 6:10~) ,libavutil-dev (>= 6:10~) + ,libswresample-dev | libavresample-dev ,libswscale-dev (>= 6:10~) + ,ffmpeg | libav-tools + ,net-tools ,libbz2-dev ,libgcrypt-dev | libgcrypt11-dev ,libcurl4-gnutls-dev @@ -27,6 +30,9 @@ Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apa ,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 @@ -39,7 +45,9 @@ Package: zoneminder Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,javascript-common - ,libmp4v2-2, libx264-142|libx264-148|libx264-152, libswscale-ffmpeg3|libswscale4|libswscale3 + ,libmp4v2-2, libx264-142|libx264-148|libx264-152|libx264-155 + ,libswscale-ffmpeg3|libswscale4|libswscale3|libswscale5 + ,libswresample2|libswresample3|libswresample24|libswresample-ffmpeg1 ,ffmpeg | libav-tools ,libdate-manip-perl, libmime-lite-perl, libmime-tools-perl ,libdbd-mysql-perl @@ -50,11 +58,12 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,libdbd-mysql-perl ,libdevice-serialport-perl ,libimage-info-perl - ,libjson-any-perl + ,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 @@ -65,11 +74,14 @@ 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 ,libpcre3 + ,libssl | libssl1.0.0 | libssl1.1 + ,libcrypt-eksblowfish-perl + ,libdata-entropy-perl Recommends: ${misc:Recommends} ,libapache2-mod-php5 | libapache2-mod-php | php5-fpm | php-fpm ,mysql-server | mariadb-server | virtual-mysql-server @@ -97,7 +109,7 @@ Description: video camera security and surveillance solution # ,libdbd-mysql-perl # ,libdevice-serialport-perl # ,libimage-info-perl -# ,libjson-any-perl +# ,libjson-maybexs-perl # ,libsys-mmap-perl [!hurd-any] # ,liburi-encode-perl # ,libwww-perl diff --git a/distros/ubuntu1604/rules b/distros/ubuntu1604/rules index f0808a8e1..c671a1b03 100755 --- a/distros/ubuntu1604/rules +++ b/distros/ubuntu1604/rules @@ -27,7 +27,7 @@ override_dh_auto_configure: -DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \ -DZM_CACHEDIR="/var/cache/zoneminder/cache" \ -DZM_DIR_EVENTS="/var/cache/zoneminder/events" \ - -DZM_DIR_IMAGES="/var/cache/zoneminder/images" \ + -DZM_PATH_SHUTDOWN="/sbin/shutdown" \ -DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" override_dh_clean: 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 17272c2df..d3983950b 100644 --- a/distros/ubuntu1604/zoneminder.postinst +++ b/distros/ubuntu1604/zoneminder.postinst @@ -22,7 +22,7 @@ if [ "$1" = "configure" ]; then if [ "$ZM_DB_HOST" = "localhost" ]; then - if [ -e "/lib/systemd/system/mysql.service" ] || [ -e "/lib/systemd/system/mariadb.service" ]; then + if [ -e "/lib/systemd/system/mysql.service" ] || [ -e "/lib/systemd/system/mariadb.service" ] || [ -e "/etc/init.d/mysql" ]; then # Ensure zoneminder is stopped deb-systemd-invoke stop zoneminder.service || exit $? @@ -56,6 +56,10 @@ if [ "$1" = "configure" ]; then if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then echo "Creating zm db" cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf + if [ $? -ne 0 ]; then + echo "Error creating db." + exit 1; + fi # This creates the user. echo "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 @@ -68,6 +72,7 @@ if [ "$1" = "configure" ]; then # Add any new PTZ control configurations to the database (will not overwrite) zmcamtool.pl --import >/dev/null 2>&1 + echo "Done Updating; starting ZoneMinder." else echo 'NOTE: MySQL/MariaDB not running; please start mysql and run dpkg-reconfigure zoneminder when it is running.' fi @@ -78,7 +83,6 @@ if [ "$1" = "configure" ]; then else echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)." fi - echo "Done Updating; starting ZoneMinder." deb-systemd-invoke restart zoneminder.service fi diff --git a/distros/ubuntu1604/zoneminder.service b/distros/ubuntu1604/zoneminder.service index ac719b733..cb2d6791e 100644 --- a/distros/ubuntu1604/zoneminder.service +++ b/distros/ubuntu1604/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/ubuntu1604/zoneminder.tmpfile b/distros/ubuntu1604/zoneminder.tmpfile index 9435be2a8..cbfdec1de 100644 --- a/distros/ubuntu1604/zoneminder.tmpfile +++ b/distros/ubuntu1604/zoneminder.tmpfile @@ -1,4 +1,4 @@ -d /var/run/zm 0755 www-data www-data +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/doc-pak/AUTHORS b/doc-pak/AUTHORS deleted file mode 100644 index 7f0362072..000000000 --- a/doc-pak/AUTHORS +++ /dev/null @@ -1,6 +0,0 @@ -ZoneMinder - A Linux based camera monitoring and analysis tool. - -This project was imagined and created by Philip Coombes in -September 2002, shortly after being burglarised of his power tools. - -mailto:philip.coombes@zoneminder.com diff --git a/doc-pak/BUGS b/doc-pak/BUGS deleted file mode 100644 index 3160e2dce..000000000 --- a/doc-pak/BUGS +++ /dev/null @@ -1 +0,0 @@ -Please see README file. diff --git a/doc-pak/COPYING b/doc-pak/COPYING deleted file mode 100644 index d60c31a97..000000000 --- a/doc-pak/COPYING +++ /dev/null @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff --git a/doc-pak/ChangeLog b/doc-pak/ChangeLog deleted file mode 100644 index 3160e2dce..000000000 --- a/doc-pak/ChangeLog +++ /dev/null @@ -1 +0,0 @@ -Please see README file. diff --git a/doc-pak/INSTALL b/doc-pak/INSTALL deleted file mode 100644 index 1de0ac2ad..000000000 --- a/doc-pak/INSTALL +++ /dev/null @@ -1,185 +0,0 @@ -Basic Installation -================== - - These are generic installation instructions. - - Please see http://www.zoneminder.com/wiki/index.php/Documentation - for full details. - - The `configure' shell script attempts to guess correct values for -various system-dependent variables used during compilation. It uses -those values to create a `Makefile' in each directory of the package. -It may also create one or more `.h' files containing system-dependent -definitions. Finally, it creates a shell script `config.status' that -you can run in the future to recreate the current configuration, a file -`config.cache' that saves the results of its tests to speed up -reconfiguring, and a file `config.log' containing compiler output -(useful mainly for debugging `configure'). - - If you need to do unusual things to compile the package, please try -to figure out how `configure' could check whether to do them, and mail -diffs or instructions to the address given in the `README' so they can -be considered for the next release. If at some point `config.cache' -contains results you don't want to keep, you may remove or edit it. - - The file `configure.in' is used to create `configure' by a program -called `autoconf'. You only need `configure.in' if you want to change -it or regenerate `configure' using a newer version of `autoconf'. - -The simplest way to compile this package is: - - 1. `cd' to the directory containing the package's source code and type - `./configure' to configure the package for your system. If you're - using `csh' on an old version of System V, you might need to type - `sh ./configure' instead to prevent `csh' from trying to execute - `configure' itself. - - Running `configure' takes awhile. While running, it prints some - messages telling which features it is checking for. - - 2. Type `make' to compile the package. - - 3. Optionally, type `make check' to run any self-tests that come with - the package. - - 4. Type `make install' to install the programs and any data files and - documentation. - - 5. You can remove the program binaries and object files from the - source code directory by typing `make clean'. To also remove the - files that `configure' created (so you can compile the package for - a different kind of computer), type `make distclean'. There is - also a `make maintainer-clean' target, but that is intended mainly - for the package's developers. If you use it, you may have to get - all sorts of other programs in order to regenerate files that came - with the distribution. - -Compilers and Options -===================== - - Some systems require unusual options for compilation or linking that -the `configure' script does not know about. You can give `configure' -initial values for variables by setting them in the environment. Using -a Bourne-compatible shell, you can do that on the command line like -this: - CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure - -Or on systems that have the `env' program, you can do it like this: - env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure - -Compiling For Multiple Architectures -==================================== - - You can compile the package for more than one kind of computer at the -same time, by placing the object files for each architecture in their -own directory. To do this, you must use a version of `make' that -supports the `VPATH' variable, such as GNU `make'. `cd' to the -directory where you want the object files and executables to go and run -the `configure' script. `configure' automatically checks for the -source code in the directory that `configure' is in and in `..'. - - If you have to use a `make' that does not supports the `VPATH' -variable, you have to compile the package for one architecture at a time -in the source code directory. After you have installed the package for -one architecture, use `make distclean' before reconfiguring for another -architecture. - -Installation Names -================== - - By default, `make install' will install the package's files in -`/usr/local/bin', `/usr/local/man', etc. You can specify an -installation prefix other than `/usr/local' by giving `configure' the -option `--prefix=PATH'. - - You can specify separate installation prefixes for -architecture-specific files and architecture-independent files. If you -give `configure' the option `--exec-prefix=PATH', the package will use -PATH as the prefix for installing programs and libraries. -Documentation and other data files will still use the regular prefix. - - In addition, if you use an unusual directory layout you can give -options like `--bindir=PATH' to specify different values for particular -kinds of files. Run `configure --help' for a list of the directories -you can set and what kinds of files go in them. - - If the package supports it, you can cause programs to be installed -with an extra prefix or suffix on their names by giving `configure' the -option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. - -Optional Features -================= - - Some packages pay attention to `--enable-FEATURE' options to -`configure', where FEATURE indicates an optional part of the package. -They may also pay attention to `--with-PACKAGE' options, where PACKAGE -is something like `gnu-as' or `x' (for the X Window System). The -`README' should mention any `--enable-' and `--with-' options that the -package recognizes. - - For packages that use the X Window System, `configure' can usually -find the X include and library files automatically, but if it doesn't, -you can use the `configure' options `--x-includes=DIR' and -`--x-libraries=DIR' to specify their locations. - -Specifying the System Type -========================== - - There may be some features `configure' can not figure out -automatically, but needs to determine by the type of host the package -will run on. Usually `configure' can figure that out, but if it prints -a message saying it can not guess the host type, give it the -`--host=TYPE' option. TYPE can either be a short name for the system -type, such as `sun4', or a canonical name with three fields: - CPU-COMPANY-SYSTEM - -See the file `config.sub' for the possible values of each field. If -`config.sub' isn't included in this package, then this package doesn't -need to know the host type. - - If you are building compiler tools for cross-compiling, you can also -use the `--target=TYPE' option to select the type of system they will -produce code for and the `--build=TYPE' option to select the type of -system on which you are compiling the package. - -Sharing Defaults -================ - - If you want to set default values for `configure' scripts to share, -you can create a site shell script called `config.site' that gives -default values for variables like `CC', `cache_file', and `prefix'. -`configure' looks for `PREFIX/share/config.site' if it exists, then -`PREFIX/etc/config.site' if it exists. Or, you can set the -`CONFIG_SITE' environment variable to the location of the site script. -A warning: not all `configure' scripts look for a site script. - -Operation Controls -================== - - `configure' recognizes the following options to control how it -operates. - -`--cache-file=FILE' - Use and save the results of the tests in FILE instead of - `./config.cache'. Set FILE to `/dev/null' to disable caching, for - debugging `configure'. - -`--help' - Print a summary of the options to `configure', and exit. - -`--quiet' -`--silent' -`-q' - Do not print messages saying which checks are being made. To - suppress all normal output, redirect it to `/dev/null' (any error - messages will still be shown). - -`--srcdir=DIR' - Look for the package's source code in directory DIR. Usually - `configure' can determine that directory automatically. - -`--version' - Print the version of Autoconf used to generate the `configure' - script, and exit. - -`configure' also accepts some other, not widely useful, options. diff --git a/doc-pak/NEWS b/doc-pak/NEWS deleted file mode 100644 index 3160e2dce..000000000 --- a/doc-pak/NEWS +++ /dev/null @@ -1 +0,0 @@ -Please see README file. diff --git a/doc-pak/README b/doc-pak/README deleted file mode 100644 index e3dfa62e1..000000000 --- a/doc-pak/README +++ /dev/null @@ -1,5 +0,0 @@ - -All documentation for ZoneMinder is now online at - -http://www.zoneminder.com/wiki/index.php/Documentation - diff --git a/doc-pak/TODO b/doc-pak/TODO deleted file mode 100644 index 69af9c87e..000000000 --- a/doc-pak/TODO +++ /dev/null @@ -1,2 +0,0 @@ -Please see README file. - 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 706748880..76eadcaa3 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,7 +1,8 @@ + API ==== -This document will provide an overview of ZoneMinder's API. This is work in progress. +This document will provide an overview of ZoneMinder's API. Overview ^^^^^^^^ @@ -13,48 +14,187 @@ 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. -Security +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 +^^^^^^^^^^^^^^^ + +The ZoneMinder API has evolved over time. Broadly speaking the iterations were as follows: + +* 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), 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. + + + +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) + +Enabling secret key +^^^^^^^^^^^^^^^^^^^ + +* It is **important** that you create a "Secret Key". This needs to be a set of hard to guess characters, that only you know. ZoneMinder does not create a key for you. It is your responsibility to create it. If you haven't created one already, please do so by going to ``Options->Systems`` and populating ``AUTH_HASH_SECRET``. Don't forget to save. +* If you plan on using V2.0 token based security, **it is mandatory to populate this secret key**, as it is used to sign the token. If you don't, token authentication will fail. V1.0 did not mandate this requirement. + + +Getting an API key +^^^^^^^^^^^^^^^^^^^^^^^ + +To get an API key: + +:: + + curl -XPOST -d "user=yourusername&pass=yourpassword" https://yourserver/zm/api/host/login.json + + +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: + +:: + + { + "credentials": "auth=05f3a50e8f7063", + "append_password": 0, + "version": "1.33.9", + "apiversion": "1.0" + } + +Or for API 2.0: + +:: + + { + "access_token": "eyJ0eXAiOiJKHE", + "access_token_expires": 3600, + "refresh_token": "eyJ0eXAiOimPs", + "refresh_token_expires": 86400, + "credentials": "auth=05f3a50e8f7063", # only if OPT_USE_LEGACY_API_AUTH is enabled + "append_password": 0, # only if OPT_USE_LEGACY_API_AUTH is enabled + "version": "1.33.9", + "apiversion": "2.0" + } + +Using these keys with subsequent requests +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Once you have the keys (a.k.a credentials (v1.0, v2.0) or token (v2.0)) you should now supply that key to subsequent API calls like this: + +:: + + # v1.0 or 2.0 based API access (will only work if AUTH_HASH_LOGINS is enabled + + # RECOMMENDED: v2.0 token based + curl -XGET https://yourserver/zm/api/monitors.json?token= + + # or, for legacy mode: + + curl -XGET https://yourserver/zm/api/monitors.json?auth= + + # or, if you specified -c cookies.txt in the original login request + + curl -b cookies.txt -XGET https://yourserver/zm/api/monitors.json + + +.. NOTE:: + + 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) +^^^^^^^^^^^^^^^^^^^^^ + +If you are using the old credentials mechanism present in v1.0, then the credentials will time out based on PHP session timeout (if you are using cookies), or the value of ``AUTH_HASH_TTL`` (if you are using ``auth=`` and have enabled ``AUTH_HASH_LOGINS``) which defaults to 2 hours. Note that there is no way to look at the hash and decipher how much time is remaining. So it is your responsibility to record the time you got the hash and assume it was generated at the time you got it and re-login before that time expires. + +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_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) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you are using V2.0, then you need to know how to use these tokens effectively: + +* Access tokens are short lived. ZoneMinder issues access tokens that live for 3600 seconds (1 hour). +* Access tokens should be used for all subsequent API accesses. +* Refresh tokens should ONLY be used to generate new access tokens. For example, if an access token lives for 1 hour, before the hour completes, invoke the ``login.json`` API above with the refresh token to get a new access token. ZoneMinder issues refresh tokens that live for 24 hours. +* To generate a new refresh token before 24 hours are up, you will need to pass your user login and password to ``login.json`` + +**To Summarize:** + +* Pass your ``username`` and ``password`` to ``login.json`` only once in 24 hours to renew your tokens +* Pass your "refresh token" to ``login.json`` once in two hours (or whatever you have set the value of ``AUTH_HASH_TTL`` to) to renew your ``access token`` +* Use your ``access token`` for all API invocations. + +In fact, V2.0 will reject your request (if it is not to ``login.json``) if it comes with a refresh token instead of an access token to discourage usage of this token when it should not be used. + +This minimizes the amount of sensitive data that is sent over the wire and the lifetime durations are made so that if they get compromised, you can regenerate or invalidate them (more on this later) + +Understanding key security +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* Version 1.0 uses an MD5 hash to generate the credentials. The hash is computed over your secret key (if available), username, password and some time parameters (along with remote IP if enabled). This is not a secure/recommended hashing mechanism. If your auth hash is compromised, an attacker will be able to use your hash till it expires. To avoid this, you could disable the user in ZoneMinder. Furthermore, enabling remote IP (``AUTH_HASH_REMOTE_IP``) requires that you issue future requests from the same IP that generated the tokens. While this may be considered an additional layer for security, this can cause issues with mobile devices. + +* Version 2.0 uses a different approach. The hash is a simple base64 encoded form of "claims", but signed with your secret key. Consider for example, the following access key: + +:: + + eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJab25lTWluZGVyIiwiaWF0IjoxNTU3OTQwNzUyLCJleHAiOjE1NTc5NDQzNTIsInVzZXIiOiJhZG1pbiIsInR5cGUiOiJhY2Nlc3MifQ.-5VOcpw3cFHiSTN5zfGDSrrPyVya1M8_2Anh5u6eNlI + +If you were to use any `JWT token verifier `__ it can easily decode that token and will show: + +:: + + { + "iss": "ZoneMinder", + "iat": 1557940752, + "exp": 1557944352, + "user": "admin", + "type": "access" + } + Invalid Signature + + +Don't be surprised. JWT tokens, by default, are `not meant to be encrypted `__. It is just an assertion of a claim. It states that the issuer of this token was ZoneMinder, +It was issued at (iat) Wednesday, 2019-05-15 17:19:12 UTC and will expire on (exp) Wednesday, 2019-05-15 18:19:12 UTC. This token claims to be owned by an admin and is an access token. If your token were to be stolen, this information is available to the person who stole it. Note that there are no sensitive details like passwords in this claim. + +However, that person will **not** have your secret key as part of this token and therefore, will NOT be able to create a new JWT token to get, say, a refresh token. They will however, be able to use your access token to access resources just like the auth hash above, till the access token expires (2 hrs). To revoke this token, you don't need to disable the user. Go to ``Options->API`` and tap on "Revoke All Access Tokens". This will invalidate the token immediately (this option will invalidate all tokens for all users, and new ones will need to be generated). + +Over time, we will provide you with more fine grained access to these options. + +**Summarizing good practices:** + +* Use HTTPS, not HTTP +* If possible, use free services like `LetsEncrypt `__ instead of self-signed certificates (sometimes this is not possible) +* Keep your tokens as private as possible, and use them as recommended above +* If you believe your tokens are compromised, revoke them, but also check if your attacker has compromised more than you think (example, they may also have your username/password or access to your system via other exploits, in which case they can regenerate as many tokens/credentials as they want). + + +.. NOTE:: + Subsequent sections don't explicitly callout the key addition to APIs. We assume that you will append the correct keys as per our explanation above. + + +Examples ^^^^^^^^^ -The APIs tie into ZoneMinder's existing security model. This means if you have -OPT_AUTH enabled, you need to log into ZoneMinder using the same browser you plan to -use the APIs from. If you are developing an app that relies on the API, you need -to do a POST login from the app into ZoneMinder before you can access the API. - -Then, you need to re-use the authentication information of the login (returned as cookie states) -with subsequent APIs for the authentication information to flow through to the APIs. - -This means if you plan to use cuRL to experiment with these APIs, you first need to do - -:: - - curl -d "username=XXXX&password=YYYY&action=login&view=console" -c cookies.txt http://yourzmip/zm/index.php - -replacing *XXXX* and *YYYY* with your username and password, respectively. - -Please make sure you do this in a directory where you have write permissions, otherwise cookies.txt will not be created -and the command will silently fail. - - -What the "-c cookies.txt" does is store a cookie state reflecting that you have logged into ZM. You now need -to apply that cookie state to all subsequent APIs. You do that by using a '-b cookies.txt' to subsequent APIs if you are -using CuRL like so: - -:: - - curl -b cookies.txt http://yourzmip/zm/api/monitors.json - -This would return a list of monitors and pass on the authentication information to the ZM API layer. - -So remember, if you are using authentication, please add a ``-b cookies.txt`` to each of the commands below if you are using -CuRL. If you are not using CuRL and writing your own app, you need to make sure you pass on cookies to subsequent requests -in your app. - -Examples (please read security notice above) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You will see each URL ending in either ``.xml`` or ``.json``. This is the -format of the request, and it determines the format that any data returned to -you will be in. I like json, however you can use xml if you'd like. (In all examples, replace 'server' with IP or hostname & port where ZoneMinder is running) @@ -74,6 +214,22 @@ Return a list of all monitors curl http://server/zm/api/monitors.json +It is worthwhile to note that starting ZM 1.32.3 and beyond, this API also returns a ``Monitor_Status`` object per monitor. It looks like this: + +:: + + "Monitor_Status": { + "MonitorId": "2", + "Status": "Connected", + "CaptureFPS": "1.67", + "AnalysisFPS": "1.67", + "CaptureBandwidth": "52095" + } + + +If you don't see this in your API, you are running an older version of ZM. This gives you a very convenient way to check monitor status without calling the ``daemonCheck`` API described later. + + Retrieve monitor 1 ^^^^^^^^^^^^^^^^^^^ @@ -90,6 +246,13 @@ This API changes monitor 1 to Modect and Enabled :: curl -XPOST http://server/zm/api/monitors/1.json -d "Monitor[Function]=Modect&Monitor[Enabled]=1" + +Get Daemon Status of Monitor 1 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + curl http://server/zm/api/monitors/daemonStatus/id:1/daemon:zmc.json Add a monitor ^^^^^^^^^^^^^^ @@ -258,6 +421,15 @@ This returns number of events per monitor that were recorded in the last day whe +Return sorted events +^^^^^^^^^^^^^^^^^^^^^^ + +This returns a list of events within a time range and also sorts it by descending order + +:: + + curl -XGET "http://server/zm/api/events/index/StartTime%20>=:2015-05-15%2018:43:56/EndTime%20<=:208:43:56.json?sort=StartTime&direction=desc" + Configuration Apis ^^^^^^^^^^^^^^^^^^^ @@ -326,11 +498,13 @@ Create a Zone &Zone[MaxBlobs]=\ &Zone[OverloadFrames]=0" -PTZ Control APIs -^^^^^^^^^^^^^^^^ +PTZ Control Meta-Data APIs +^^^^^^^^^^^^^^^^^^^^^^^^^^^ PTZ controls associated with a monitor are stored in the Controls table and not the Monitors table inside ZM. What that means is when you get the details of a Monitor, you will only know if it is controllable (isControllable:true) and the control ID. To be able to retrieve PTZ information related to that Control ID, you need to use the controls API +Note that these APIs only retrieve control data related to PTZ. They don't actually move the camera. See the "PTZ on live streams" section to move the camera. + This returns all the control definitions: :: @@ -348,7 +522,192 @@ ZM APIs have various APIs that help you in determining host (aka ZM) daemon stat :: - curl -XGET http://server/zm/api/host/daemonCheck.json # 1 = ZM running 0=not running curl -XGET http://server/zm/api/host/getLoad.json # returns current load of ZM - curl -XGET http://server/zm/api/host/getDiskPercent.json # returns in GB (not percentage), disk usage per monitor (that is, space taken to store various event related information,images etc. per monitor) + + # Note that ZM 1.32.3 onwards has the same information in Monitors.json which is more reliable and works for multi-server too. + curl -XGET http://server/zm/api/host/daemonCheck.json # 1 = ZM running 0=not running + + # The API below uses "du" to calculate disk space. We no longer recommend you use it if you have many events. Use the Storage APIs instead, described later + curl -XGET http://server/zm/api/host/getDiskPercent.json # returns in GB (not percentage), disk usage per monitor (that is,space taken to store various event related information,images etc. per monitor) + + +Storage and Server APIs +^^^^^^^^^^^^^^^^^^^^^^^ + +ZoneMinder introduced many new options that allowed you to configure multiserver/multistorage configurations. While a part of this was available in previous versions, a lot of rework was done as part of ZM 1.31 and 1.32. As part of that work, a lot of new and useful APIs were added. Some of these are part of ZM 1.32 and others will be part of ZM 1.32.3 (of course, if you build from master, you can access them right away, or wait till a stable release is out. + + + +This returns storage data for my single server install. If you are using multi-storage, you'll see many such "Storage" entries, one for each storage defined: + +:: + + curl http://server/zm/api/storage.json + +Returns: + +:: + + { + "storage": [ + { + "Storage": { + "Id": "0", + "Path": "\/var\/cache\/zoneminder\/events", + "Name": "Default", + "Type": "local", + "Url": null, + "DiskSpace": "364705447651", + "Scheme": "Medium", + "ServerId": null, + "DoDelete": true + } + } + ] + } + + + +"DiskSpace" is the disk used in bytes. While this doesn't return disk space data as rich as ``/host/getDiskPercent``, it is much more efficient. + +Similarly, + +:: + + curl http://server/zm/api/servers.json + +Returns: + +:: + + { + "servers": [ + { + "Server": { + "Id": "1", + "Name": "server1", + "Hostname": "server1.mydomain.com", + "State_Id": null, + "Status": "Running", + "CpuLoad": "0.9", + "TotalMem": "6186237952", + "FreeMem": "156102656", + "TotalSwap": "536866816", + "FreeSwap": "525697024", + "zmstats": false, + "zmaudit": false, + "zmtrigger": false + } + } + ] + } + +This only works if you have a multiserver setup in place. If you don't it will return an empty array. + +Other APIs +^^^^^^^^^^ +This is not a complete list. ZM supports more parameters/APIs. A good way to dive in is to look at the `API code `__ directly. + +Streaming Interface +^^^^^^^^^^^^^^^^^^^ +Developers working on their application often ask if there is an "API" to receive live streams, or recorded event streams. +It is possible to stream both live and recorded streams. This isn't strictly an "API" per-se (that is, it is not integrated +into the Cake PHP based API layer discussed here) and also why we've used the term "Interface" instead of an "API". + +Live Streams +~~~~~~~~~~~~~~ +What you need to know is that if you want to display "live streams", ZoneMinder sends you streaming JPEG images (MJPEG) +which can easily be rendered in a browser using an ``img src`` tag. + +For example: + +:: + + + + # or + + + + + + +will display a live feed from monitor id 1, scaled down by 50% in quality and resized to 640x480px. + +* This assumes ``/zm/cgi-bin`` is your CGI_BIN path. Change it to what is correct in your system +* The "auth" token you see above is required if you use ZoneMinder authentication. To understand how to get the auth token, please read the "Login, Logout & API security" section below. +* The "connkey" parameter is essentially a random number which uniquely identifies a stream. If you don't specify a connkey, ZM will generate its own. It is recommended to generate a connkey because you can then use it to "control" the stream (pause/resume etc.) +* Instead of dealing with the "auth" token, you can also use ``&user=username&pass=password`` where "username" and "password" are your ZoneMinder username and password respectively. Note that this is not recommended because you are transmitting them in a URL and even if you use HTTPS, they may show up in web server logs. + + +PTZ on live streams +------------------- +PTZ commands are pretty cryptic in ZoneMinder. This is not meant to be an exhaustive guide, but just something to whet your appetite: + + +Lets assume you have a monitor, with ID=6. Let's further assume you want to pan it left. + +You'd need to send a: +``POST`` command to ``https://yourserver/zm/index.php`` with the following data payload in the command (NOT in the URL) + +``view=request&request=control&id=6&control=moveConLeft&xge=30&yge=30`` + +Obviously, if you are using authentication, you need to be logged in for this to work. + +Like I said, at this stage, this is only meant to get you started. Explore the ZoneMinder code and use "Inspect source" as you use PTZ commands in the ZoneMinder source code. +`control_functions.php `__ is a great place to start. + + +Pre-recorded (past event) streams +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Similar to live playback, if you have chosen to store events in JPEG mode, you can play it back using: + +:: + + + + # or + + + + + +* This assumes ``/zm/cgi-bin`` is your CGI_BIN path. Change it to what is correct in your system +* This will playback event 293820, starting from frame 1 as an MJPEG stream +* Like before, you can add more parameters like ``scale`` etc. +* auth and connkey have the same meaning as before, and yes, you can replace auth by ``&user=usename&pass=password`` as before and the same security concerns cited above apply. + +If instead, you have chosen to use the MP4 (Video) storage mode for events, you can directly play back the saved video file: + +:: + + + + + # or + + + + +This above will play back the video recording for event 294690 + +What other parameters are supported? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The best way to answer this question is to play with ZoneMinder console. Open a browser, play back live or recorded feed, and do an "Inspect Source" to see what parameters +are generated. Change and observe. + + + +Further Reading +^^^^^^^^^^^^^^^^ + +As described earlier, treat this document as an "introduction" to the important parts of the API and streaming interfaces. +There are several details that haven't yet been documented. Till they are, here are some resources: + +* zmNinja, the open source mobile app for ZoneMinder is 100% based on ZM APIs. Explore its `source code `__ to see how things work. +* Launch up ZM console in a browser, and do an "Inspect source". See how images are being rendered. Go to the networks tab of the inspect source console and look at network requests that are made when you pause/play/forward streams. +* If you still can't find an answer, post your question in the `forums `__ (not the github repo). + + 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 7735ff38f..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? --------------------------------------- @@ -311,12 +220,12 @@ There are a number of specific reasons why processor loads can be high either by The main causes are. - * Using a video palette other than greyscale or RGB24. This can cause a relatively minor performace hit, though still significant. Although some cameras and cards require using planar palettes ZM currently doesn't support this format internally and each frame is converted to an RGB representation prior to processing. Unless you have compelling reasons for using YUV or reduced RGB type palettes such as hitting USB transfer limits I would experiment to see if RGB24 or greyscale is quicker. Put your monitors into 'Monitor' mode so that only the capture daemons are running and monitor the process load of these (the 'zmc' processes) using top. Try it with various palettes to see if it makes a difference. + * Using a video palette other than greyscale or RGB24. This can cause a relatively minor performance hit, though still significant. Although some cameras and cards require using planar palettes ZM currently doesn't support this format internally and each frame is converted to an RGB representation prior to processing. Unless you have compelling reasons for using YUV or reduced RGB type palettes such as hitting USB transfer limits I would experiment to see if RGB24 or greyscale is quicker. Put your monitors into 'Monitor' mode so that only the capture daemons are running and monitor the process load of these (the 'zmc' processes) using top. Try it with various palettes to see if it makes a difference. * 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,12 +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're using Linux, this can be found using the following command: + +:: + + timedatectl | grep "Time zone" + +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 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. @@ -658,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? @@ -698,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 @@ -728,6 +510,8 @@ What causes "Invalid JPEG file structure: two SOI markers" from zmc (1.24.x) Some settings that used to be global only are now per camera. On the Monitor Source tab, if you are using Remote Protocol "HTTP" and Remote Method "Simple", try changing Remote Method to "Regexp". + + Miscellaneous ------------------- I see ZoneMinder is licensed under the GPL. What does that allow or restrict me in doing with ZoneMinder? @@ -741,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 0b926435f..74a9ca81d 100644 --- a/docs/installationguide/debian.rst +++ b/docs/installationguide/debian.rst @@ -3,6 +3,146 @@ Debian .. contents:: +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. + +**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 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. + +:: + + sudo nano /etc/apt/sources.list + +Add the following to the bottom of the file + +:: + + # ZoneMinder repository + deb https://zmrepo.zoneminder.com/debian/release stretch/ + +CTRL+o and to save +CTRL+x to exit + +Because ZoneMinder's package repository provides a secure connection through HTTPS, apt must be enabled for HTTPS. +:: + + sudo apt install apt-transport-https + +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 + 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.0/apache2/php.ini + +Manual way +:: + + sudo 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. 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 Jessie ----------------------- @@ -49,11 +189,17 @@ Add the following to the bottom of the file :: # Backports repository - deb http://httpredir.debian.org/debian jessie-backports main contrib non-free + deb http://archive.debian.org/debian/ jessie-backports main contrib non-free CTRL+o and to save CTRL+x to exit +Run the following + +:: + + echo 'Acquire::Check-Valid-Until no;' > /etc/apt/apt.conf.d/99no-check-valid-until + **Step 5:** Install ZoneMinder :: @@ -68,8 +214,7 @@ a read. :: - gunzip /usr/share/doc/zoneminder/README.Debian.gz - cat /usr/share/doc/zoneminder/README.Debian + zcat /usr/share/doc/zoneminder/README.Debian.gz **Step 7:** Setup Database @@ -81,7 +226,7 @@ should you choose to change default database user and password. 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,index on zm.* to 'zmuser'@localhost identified by "zmpass";' | sudo mysql --defaults-file=/etc/mysql/debian.cnf mysql -** Step 8:** zm.conf Permissions +**Step 8:** zm.conf Permissions Adjust permissions to the zm.conf file to allow web account to access it. @@ -91,7 +236,7 @@ Adjust permissions to the zm.conf file to allow web account to access it. **Step 9:** Setup ZoneMinder service -:: + :: systemctl enable zoneminder.service @@ -127,7 +272,24 @@ CTRL+o then [Enter] to save CTRL+x to exit -**Step 12:** Start ZoneMinder + +**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 + + 2. Check config of /etc/apache2/conf-enabled/zoneminder.conf has the same ScriptAlias /zm/cgi-bin that is configured + in ZM_PATH. The part /nph-zms has to be left out of the ScriptAlias + + ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin" + + + :: + cat /etc/apache2/conf-enabled/zoneminder.conf + +**Step 13:** Start ZoneMinder Reload Apache to enable your changes and then start ZoneMinder. @@ -136,7 +298,7 @@ Reload Apache to enable your changes and then start ZoneMinder. systemctl reload apache2 systemctl start zoneminder -**Step 13:** Making sure ZoneMinder works +**Step 14:** Making sure ZoneMinder works 1. Open up a browser and go to ``http://hostname_or_ip/zm`` - should bring up ZoneMinder Console diff --git a/docs/installationguide/dedicateddrive.rst b/docs/installationguide/dedicateddrive.rst index f0dfadc28..6a7367b8b 100644 --- a/docs/installationguide/dedicateddrive.rst +++ b/docs/installationguide/dedicateddrive.rst @@ -31,7 +31,6 @@ Add the following content to the file and save your changes: :: ZM_DIR_EVENTS=/full/path/to/the/events/folder - ZM_DIR_IMAGES=/full/path/to/the/images/folder **Step 5:** Start ZoneMinder and inspect the ZoneMinder log files for errors. :: 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 12d9ed769..d35f1fbd3 100644 --- a/docs/installationguide/redhat.rst +++ b/docs/installationguide/redhat.rst @@ -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`_. @@ -158,7 +158,7 @@ The list of available Mock config files are available here: ls /etc/mock/*rpmfusion_free.cfg -You choose the config file based on the desired distro (e.g. el6, el7, f20, f21) and basearch (e.g. x86, x86_64, arhmhfp). Notice that, when specifying the Mock config as a commandline parameter, you should leave off the ".cfg" filename extension. +You choose the config file based on the desired distro (e.g. el7, f29, f30) and basearch (e.g. x86, x86_64, arhmhfp). Notice that, when specifying the Mock config as a commandline parameter, you should leave off the ".cfg" filename extension. Installation ************ @@ -188,8 +188,8 @@ Now clone the ZoneMinder git repository from your home folder: :: cd - git clone https://github.com/ZoneMinder/ZoneMinder - cd ZoneMinder + 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. @@ -197,27 +197,27 @@ If you have previsouly cloned the ZoneMinder git repo and wish to update it to t :: - cd ~\ZoneMinder + cd ~/zoneminder git pull origin master Get the crud submodule tarball: :: - spectool -f -g -R -s 1 ~/ZoneMinder/distros/redhat/zoneminder.spec + spectool -f -g -R -s 1 ~/zoneminder/distros/redhat/zoneminder.spec At this point, you can make changes to the source code. Depending on what you want to do with those changes, you generally want to create a new branch first: :: - cd ~\ZoneMinder + cd ~/zoneminder git checkout -b mynewbranch Again, depending on what you want to do with those changes, you may want to commit your changes: :: - cd ~\ZoneMinder + cd ~/zoneminder git add . git commit @@ -225,28 +225,28 @@ Once you have made your changes, it is time to turn your work into a new tarball :: - less ~/ZoneMinder/distros/redhat/zoneminder.spec + less ~/zoneminder/distros/redhat/zoneminder.spec Scroll down until you see the Version field. Note the value, which will be in the format x.xx.x. Now create the tarball with the following command: :: - cd ~\ZoneMinder - git archive --prefix=ZoneMinder-1.31.1/ -o ~/rpmbuild/SOURCES/zoneminder-1.31.1.tar.gz HEAD + cd ~/zoneminder + git archive --prefix=zoneminder-1.33.4/ -o ~/rpmbuild/SOURCES/zoneminder-1.33.4.tar.gz HEAD -Replace "1.31.1" with the Version shown in the rpm specfile. +Replace "1.33.4" with the Version shown in the rpm specfile. From the root of the local ZoneMinder git repo, execute the following: :: - cd ~\ZoneMinder + cd ~/zoneminder rpmbuild -bs --nodeps distros/redhat/zoneminder.spec This step will create a source rpm and it will tell you where it was saved. For example: :: - Wrote: /home/abauer/rpmbuild/SRPMS/zoneminder-1.31.1-1.fc26.src.rpm + Wrote: /home/abauer/rpmbuild/SRPMS/zoneminder-1.33.4-1.fc26.src.rpm Now follow the previous instructions `Build from SRPM`_ which describe how to build that source rpm into an rpm. diff --git a/docs/installationguide/ubuntu.rst b/docs/installationguide/ubuntu.rst index e7c821838..491ee1eb8 100644 --- a/docs/installationguide/ubuntu.rst +++ b/docs/installationguide/ubuntu.rst @@ -3,9 +3,9 @@ Ubuntu .. contents:: -Easy Way: Ubuntu 16.04 ----------------------- -These instructions are for a brand new ubuntu 16.04 system which does not have ZM +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. @@ -35,13 +36,13 @@ guide you with a quick search. `releases page `_ for the latest release. - Alternatively, the ZoneMinder project team maintains a ppa, which is updated immediately + Alternatively, the ZoneMinder project team maintains a `PPA `_, which is updated immediately following a new release of ZoneMinder. To use this repository instead of the official Ubuntu repository, enter the following from the command line: :: - add-apt-repository ppa:iconnor/zoneminder + add-apt-repository ppa:iconnor/zoneminder-1.32 Update repo and upgrade. @@ -51,6 +52,7 @@ Update repo and upgrade. apt-get upgrade apt-get dist-upgrade + **Step 3:** Configure MySQL .. sidebar :: Note @@ -62,8 +64,10 @@ Update repo and upgrade. | /etc/alternatives/my.cnf -> /etc/mysql/mysql.cnf | /etc/mysql/mysql.cnf is a basic file -Certain new defaults in MySQL 5.7 are currently causing some issues with ZoneMinder, -the workaround is to modify the sql_mode setting of MySQL. +Certain new defaults in MySQL 5.7 cause some issues with ZoneMinder < 1.32.0, +the workaround is to modify the sql_mode setting of MySQL. Please note that these +changes are NOT required for ZoneMinder 1.32.0 and some people have reported them +causing problems in 1.32.0. To better manage the MySQL server it is recommended to copy the sample config file and replace the default my.cnf symbolic link. @@ -104,10 +108,12 @@ Restart MySQL **Step 5:** Configure the ZoneMinder Database +This step should not be required on ZoneMinder 1.32.0. + :: mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql - mysql -uroot -p -e "grant select,insert,update,delete,create,alter,index,lock tables on zm.* to 'zmuser'@localhost identified by 'zmpass';" + mysql -uroot -p -e "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on zm.* to 'zmuser'@localhost identified by 'zmpass';" **Step 6:** Set permissions @@ -124,9 +130,16 @@ Set /etc/zm/zm.conf to root:www-data 740 and www-data access to content :: - a2enconf zoneminder - a2enmod cgi + a2enmod cgi a2enmod rewrite + a2enconf zoneminder + +You may also want to enable to following modules to improve caching performance + +:: + + a2enmod expires + a2enmod headers **Step 8:** Enable and start Zoneminder @@ -139,10 +152,10 @@ Set /etc/zm/zm.conf to root:www-data 740 and www-data access to content :: - nano /etc/php/7.0/apache2/php.ini + 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** :: @@ -181,8 +194,199 @@ 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 16.04 (Xenial) +------------------------------- +These instructions are for a brand new ubuntu 16.04 system which does not have ZM +installed. + + +It is recommended that you use an Ubuntu Server install and select the LAMP option +during install to install Apache, MySQL and PHP. If you failed to do this you can +achieve the same result by running: + +:: + + sudo tasksel install lamp-server + +During installation it will ask you to set up a master/root password for the MySQL. +Installing LAMP is not ZoneMinder specific so you will find plenty of resources to +guide you with a quick search. + +**Step 1:** Either run commands in this install using sudo or use the below to become root +:: + + sudo -i + +**Step 2:** Update Repos + +.. topic :: Latest Release + + ZoneMinder is now part of the current standard Ubuntu repository, but + sometimes the official repository can lag behind. To find out check our + `releases page `_ for + the latest release. + + Alternatively, the ZoneMinder project team maintains a `PPA `_, which is updated immediately + following a new release of ZoneMinder. To use this repository instead of the + official Ubuntu repository, enter the following from the command line: + + :: + + add-apt-repository ppa:iconnor/zoneminder + add-apt-repository ppa:iconnor/zoneminder-1.32 + +Update repo and upgrade. + +:: + + apt-get update + apt-get upgrade + apt-get dist-upgrade + + +**Step 3:** Configure MySQL + +.. sidebar :: Note + + The MySQL default configuration file (/etc/mysql/mysql.cnf)is read through + several symbolic links beginning with /etc/mysql/my.cnf as follows: + + | /etc/mysql/my.cnf -> /etc/alternatives/my.cnf + | /etc/alternatives/my.cnf -> /etc/mysql/mysql.cnf + | /etc/mysql/mysql.cnf is a basic file + +Certain new defaults in MySQL 5.7 cause some issues with ZoneMinder < 1.32.0, +the workaround is to modify the sql_mode setting of MySQL. Please note that these +changes are NOT required for ZoneMinder 1.32.0 and some people have reported them +causing problems in 1.32.0. + +To better manage the MySQL server it is recommended to copy the sample config file and +replace the default my.cnf symbolic link. + +:: + + rm /etc/mysql/my.cnf (this removes the current symbolic link) + cp /etc/mysql/mysql.conf.d/mysqld.cnf /etc/mysql/my.cnf + +To change MySQL settings: + +:: + + nano /etc/mysql/my.cnf + +In the [mysqld] section add the following + +:: + + sql_mode = NO_ENGINE_SUBSTITUTION + +CTRL+o then [Enter] to save + +CTRL+x to exit + +Restart MySQL + +:: + + systemctl restart mysql + + +**Step 4:** Install ZoneMinder + +:: + + apt-get install zoneminder + +**Step 5:** Configure the ZoneMinder Database + +This step should not be required on ZoneMinder 1.32.0. + +:: + + mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql + mysql -uroot -p -e "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on zm.* to 'zmuser'@localhost identified by 'zmpass';" + + +**Step 6:** Set permissions + +Set /etc/zm/zm.conf to root:www-data 740 and www-data access to content + +:: + + chmod 740 /etc/zm/zm.conf + chown root:www-data /etc/zm/zm.conf + chown -R www-data:www-data /usr/share/zoneminder/ + +**Step 7:** Configure Apache correctly: + +:: + + a2enmod cgi + a2enmod rewrite + a2enconf zoneminder + +You may also want to enable to following modules to improve caching performance + +:: + + a2enmod expires + a2enmod headers + +**Step 8:** Enable and start Zoneminder + +:: + + systemctl enable zoneminder + systemctl start zoneminder + +**Step 9:** Edit Timezone in PHP + +:: + + 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](https://www.php.net/manual/en/timezones.php). +**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:** Reload Apache service + +:: + + systemctl reload apache2 + +**Step 11:** Making sure ZoneMinder works + +1. Open up a browser and go to ``http://hostname_or_ip/zm`` - should bring up ZoneMinder Console + +2. (Optional API Check)Open up a tab in the same browser and go to ``http://hostname_or_ip/zm/api/host/getVersion.json`` + + If it is working correctly you should get version information similar to the example below: + + :: + + { + "version": "1.29.0", + "apiversion": "1.29.0.1" + } + +**Congratulations** Your installation is complete + +PPA install may need some tweaking of ZMS_PATH in ZoneMinder options. `Socket_sendto or no live streaming`_ + +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 @@ -230,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 8f85feb98..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,9 +60,14 @@ 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 Maxium FPS field. Ignoring these limitations will produce undesriable results. + **IMPORTANT:** This field is subject to the same limitations as the Maximum FPS field. Ignoring these limitations will produce undesriable results. Reference Image Blend %ge Each analysed image in ZoneMinder is a composite of previous images and is formed by applying the current image as a certain percentage of the previous reference image. Thus, if we entered the value of 10 here, each image’s part in the reference image will diminish by a factor of 0.9 each time round. So a typical reference image will be 10% the previous image, 9% the one before that and then 8.1%, 7.2%, 6.5% and so on of the rest of the way. An image will effectively vanish around 25 images later than when it was added. This blend value is what is specified here and if higher will make slower progressing events less detectable as the reference image would change more quickly. Similarly events will be deemed to be over much sooner as the reference image adapts to the new images more quickly. In signal processing terms the higher this value the steeper the event attack and decay of the signal. It depends on your particular requirements what the appropriate value would be for you but start with 10 here and adjust it (usually down) later if necessary. @@ -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 @@ -150,7 +159,7 @@ Orientation WebSite ^^^^^^^ -This Source Type allows one to configure an arbitrary website as a non-reocrdable, fully interactive, monitor in ZoneMinder. Note that sites with self-signed certificates will not display until the end user first manually navigates to the site and accpets the unsigned certificate. Also note that some sites will set an X-Frame option in the header, which discourages their site from being displayed within a frame. ZoneMinder will detect this condition and present a warning in the log. When this occurs, the end user can choose to install a browser plugin or extension to workaround this issue. +This Source Type allows one to configure an arbitrary website as a non-recordable, fully interactive, monitor in ZoneMinder. Note that sites with self-signed certificates will not display until the end user first manually navigates to the site and accpets the unsigned certificate. Also note that some sites will set an X-Frame option in the header, which discourages their site from being displayed within a frame. ZoneMinder will detect this condition and present a warning in the log. When this occurs, the end user can choose to install a browser plugin or extension to workaround this issue. Website URL Enter the full http or https url to the desired website. @@ -164,6 +173,29 @@ Height (pixels) Web Site Refresh If the website in question has static content, optionally enter a time period in seconds for ZoneMinder to refresh the content. +Storage Tab +----------- + +The storage section allows for each monitor to configure if and how video and audio are recorded. + +Save JPEGs + Records video in individual JPEG frames. Storing JPEG frames requires more storage space than h264 but it allows to view an event anytime while it is being recorded. + + * Disabled – video is not recorded as JPEG frames. If this setting is selected, then "Video Writer" should be enabled otherwise there is no video recording at all. + * Frames only – video is recorded in individual JPEG frames. + * Analysis images only (if available) – video is recorded in invidual JPEG frames with an overlay of the motion detection analysis information. Note that this overlay remains permanently visible in the frames. + * Frames + Analysis images (if available) – video is recorded twice, once as normal individual JPEG frames and once in invidual JPEG frames with analysis information overlaid. + +Video Writer + Records video in real video format. It provides much better compression results than saving JPEGs, thus longer video history can be stored. + + * Disabled – video is not recorded in video format. If this setting is selected, then "Save JPEGs" should be enabled otherwise there is no video recording at all. + * X264 Encode – the video or picture frames received from the camera are transcoded into h264 and stored as a video. This option is useful if the camera cannot natively stream h264. + * H264 Camera Passthrough – this option assumes that the camera is already sending an h264 stream. Video will be recorded as is, without any post-processing in zoneminder. Video characteristics such as bitrate, encoding mode, etc. should be set directly in the camera. + +Recording Audio + Check the box labeled "Whether to store the audio stream when saving an event." in order to save audio (if available) when events are recorded. + Timestamp Tab ------------- @@ -182,7 +214,8 @@ Warm-up Frames Pre/Post Event Image Buffer These options determine how many frames from before and after an event should be preserved with it. This allows you to view what happened immediately prior and subsequent to the event. A value of 10 for both of these will get you started but if you get a lot of short events and would prefer them to run together to form fewer longer ones then increase the Post Event buffer size. The pre-event buffer is a true buffer and should not really exceed half the ring buffer size. However the post-event buffer is just a count that is applied to captured frames and so can be managed more flexibly. You should also bear in mind the frame rate of the camera when choosing these values. For instance a network camera capturing at 1FPS will give you 10 seconds before and after each event if you chose 10 here. This may well be too much and pad out events more than necessary. However a fast video card may capture at 25FPS and you will want to ensure that this setting enables you to view a reasonable time frame pre and post event. Stream Replay Image Buffer - This option ... + The number of frames buffered to allow pausing and rewinding of the stream when live viewing a monitor. A value of 0 disables the feature. + Frames are buffered to ZM_PATH_SWAP. If this path points to a physical drive, a lot of IO will be caused during live view / montage. If you experience high system load in those situations, either disable the feature or use a RAM drive for ZM_PATH_SWAP. Alarm Frame Count This option allows you to specify how many consecutive alarm frames must occur before an alarm event is generated. The usual, and default, value is 1 which implies that any alarm frame will cause or participate in an event. You can enter any value up to 16 here to eliminate bogus events caused perhaps by screen flickers or other transients. Values over 3 or 4 are unlikely to be useful however. Please note that if you have statistics recording enabled then currently statistics are not recorded for the first ‘Alarm Frame Count’-1 frames of an event. So if you set this value to 5 then the first 4 frames will be missing statistics whereas the more usual value of 1 will ensure that all alarm frames have statistics recorded. @@ -247,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 cf33f44c9..1d00c71b4 100644 --- a/docs/userguide/definezone.rst +++ b/docs/userguide/definezone.rst @@ -29,7 +29,7 @@ Type Triggers an alarm when motion is detected within it, as long as no alarms have already been triggered in an Active zone. This is the most specialized of the zone types. For instance in the camera covering my garden I keep watch for a hedgehog that visits most nights and scoffs the food out of my cats bowls. By creating a sensitive Exclusive zone in that area I can ensure that a hedgehog alarm will only trigger if there is activity in that small area. If something much bigger occurs, like someone walking by it will trigger a regular alarm and not one from the Exclusive zone. Thus I can ensure I get alarms for big events and also special small events but not the noise in between. * Preclusive - This zone type is relatively recent. It is called a Preclusive zone because if it is triggered it actually precludes an alarm being generated for that image frame. So motion or other changes that occur in a Preclusive zone will have the effect of ensuring that no alarm occurs at all. The application for this zone type is primarily as a shortcut for detecting general large-scale lighting or other changes. Generally this may be achieved by limiting the maximum number of alarm pixels or other measure in an Active zone. However in some cases that zone may cover an area where the area of variable illumination occurs in different places as the sun and/or shadows move and it thus may be difficult to come up with general values. Additionally, if the sun comes out rapidly then although the initial change may be ignored in this way as the reference image catches up an alarm may ultimately be triggered as the image becomes less different. Using one or more Preclusive zones offers a different approach. Preclusive zones are designed to be fairly small, even just a few pixels across, with quite low alarm thresholds. They should be situated in areas of the image that are less likely to have motion occur such as high on a wall or in a corner. Should a general illumination change occur they would be triggered at least as early as any Active zones and prevent any other zones from generating an alarm. Obviously careful placement is required to ensure that they do not cancel any genuine alarms or that they are not so close together that any motion just hops from one Preclusive zone to another. Preclusive zones may also be used to reduce processing time by situating one over an Active zone. The Preclusive zone is processed first; if it is small, and is triggered, the rest of the zone/image will not be processed. + This zone type is relatively recent. It is called a Preclusive zone because if it is triggered it actually precludes an alarm being generated for that image frame. So motion or other changes that occur in a Preclusive zone will have the effect of ensuring that no alarm occurs at all. The application for this zone type is primarily as a shortcut for detecting general large-scale lighting or other changes. Generally this may be achieved by limiting the maximum number of alarm pixels or other measure in an Active zone. However in some cases that zone may cover an area where the area of variable illumination occurs in different places as the sun and/or shadows move and it thus may be difficult to come up with general values. Additionally, if the sun comes out rapidly then although the initial change may be ignored in this way as the reference image catches up an alarm may ultimately be triggered as the image becomes less different. Using one or more Preclusive zones offers a different approach. Preclusive zones are designed to be fairly small, even just a few pixels across, with quite low alarm thresholds. They should be situated in areas of the image that are less likely to have motion occur such as high on a wall or in a corner. Should a general illumination change occur they would be triggered at least as early as any Active zones and prevent any other zones from generating an alarm. Obviously careful placement is required to ensure that they do not cancel any genuine alarms or that they are not so close together that any motion just hops from one Preclusive zone to another. Preclusive zones may also be used to reduce processing time by situating one over an Active zone. The Preclusive zone is processed first; if it is small, and is triggered, the rest of the zone/image will not be processed. See Extend Alarm Frame Count below for a way to hold the preclusive zone active for an extended period. * Inactive Suppresses the detection of motion within it. This can be layered on top of any other zone type, preventing motion within the Inactive zone from being effective for any other zone type. Use inactive zones to cover areas in which nothing notable will ever happen or where you get false alarms that don't relate to what you are trying to monitor. Inactive zones may be overlaid on other zones to blank out areas, and are processed first (with the exception of Privacy zones, see below). As a general practice, you should try and make zones abut each other instead of overlapping to avoid repeated duplicate processing of the same area. @@ -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. @@ -104,7 +104,10 @@ Overload Frame Ignore Count * Number of Blobs > Max Blobs The idea is that after a change like a light going on that is considered too big to count as an alarm, it could take a couple of frames for things to settle down again. +Extend Alarm Frame Count + This field applies to Preclusive Zones only. Placing a value in this field holds the Preclusive zone active for the specified number of frames after the initial triggering event. This is useful in cases where a sudden change in light level triggers the Preclusive zone, but the zone needs to be held active for a few frames as the camera itself adjusts to that change in light level. + 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..8359aaf89 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,75 @@ 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 + * %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 + * %MOD% Path to image containing object detection + * %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 +97,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..226da8a41 100644 --- a/docs/userguide/gettingstarted.rst +++ b/docs/userguide/gettingstarted.rst @@ -5,32 +5,144 @@ 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 gor 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 + +.. 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 +163,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/introduction.rst b/docs/userguide/introduction.rst index 8da67a5e9..a6573a24d 100644 --- a/docs/userguide/introduction.rst +++ b/docs/userguide/introduction.rst @@ -1,15 +1,14 @@ Introduction ============ -Welcome to ZoneMinder, the all-in-one Linux GPL'd security camera solution. +Welcome to ZoneMinder, the all-in-one security camera solution for Linux with GPL License. -Most commercial "security systems" are designed as a monitoring system that also records. Recording quality can vary from bad to unusable, locating the relevant video can range from challenging to impractical, and exporting can often only be done with the manual present. ZoneMinder was designed primarily to record, and allow easy searches and exporting. Recordings are of the best possible quality, easy to filter and find, and simple to export using any system with a web browser. It also monitors. +Commercial "security systems" are often designed as a monitoring system with little attention to recording quality. In such a system, locating and exporting relevant video can be challenging and often requires extensive human intervention. ZoneMinder was designed to provide the best possible record quality while allowing easy searching, filtering and exporting of security footage. -ZoneMinder is designed around a series of independent components that only function when necessary limiting any wasted resource and maximising the efficiency of your machine. A fairly ancient Pentium II PC should be able to track one camera per device at up to 25 frames per second with this dropping by half approximately for each additional camera on the same device. Additional cameras on other devices do not interact so can maintain this frame rate. Even monitoring several cameras still will not overload the CPU as frame processing is designed to synchronise with capture and not stall it. +ZoneMinder is designed around a series of independent components that only function when necessary, limiting any wasted resource and maximising the efficiency of your machine. An outdated Pentium II PC can have multiple recording devices connected to it, and it is able to track one camera per device at up to 25 frames per second, which drops by approximately half for each additional camera on the same device. Additional cameras on devices that do not interact with other devices can maintain the 25 frame rate per second. Monitoring several cameras will not overload the CPU as frame processing is designed to synchronise with capture. -As well as being fast ZoneMinder is designed to be friendly and even more than that, actually useful. As well as the fast video interface core it also comes with a user friendly and comprehensive PHP based web interface allowing you to control and monitor your cameras from home, at work, on the road, or even a web enabled cell phone. It supports variable web capabilities based on available bandwidth. The web interface also allows you to view events that your cameras have captured and archive them or review them time and again, or delete the ones you no longer wish to keep. The web pages directly interact with the core daemons ensuring full co-operation at all times. ZoneMinder can even be installed as a system service ensuring it is right there if your computer has to reboot for any reason. +A fast video interface core, a user-friendly and comprehensive PHP based web interface allows ZoneMinder to be efficient, friendly and most importantly useful. You can control and monitor your cameras from home, at work, on the road, or a web-enabled cell phone. It supports variable web capabilities based on available bandwidth. The web interface also allows you to view events that your cameras have captured, which can be archived, reviewed or deleted. The web application directly interacts with the core daemons ensuring full co-operation at all times. ZoneMinder can also be installed as a system service to reboot a system remotely. -The core of ZoneMinder is the capture and analysis of images and there is a highly configurable set of parameters that allow you to ensure that you can eliminate false positives whilst ensuring that anything you don't want to miss will be captured and saved. ZoneMinder allows you to define a set of 'zones' for each camera of varying sensitivity and functionality. This allows you to eliminate regions that you don't wish to track or define areas that will alarm if various thresholds are exceeded in conjunction with other zones. - -ZoneMinder is free, but if you do find it useful then please feel free to visit http://www.zoneminder.com/donate.html and help to fund future improvements to ZoneMinder. +The core of ZoneMinder is the capture and analysis of images and a highly configurable set of parameters that eliminate false positives whilst ensuring minimum loss of footage. For example, you can define a set of 'zones' for each camera of varying sensitivity and functionality. This eliminates zones that you don't wish to track or define areas that will alarm if various thresholds are exceeded in conjunction with other zones. +ZoneMinder is free under GPL License, but if you do find it useful, then please feel free to visit https://zoneminder.com/donate/ and help us fund our future improvements. 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 '; - Error ('Invalid recaptcha secret detected'); - } - } - } // end if success==false - - } // end if using reCaptcha - - $username = validStr( $_REQUEST['username'] ); - $password = isset($_REQUEST['password'])?validStr($_REQUEST['password']):''; - userLogin( $username, $password ); - $refreshParent = true; - $view = 'console'; - $redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=console'; -} else if ( $action == 'logout' ) { - userLogout(); - $refreshParent = true; - $view = 'none'; -} else if ( $action == 'bandwidth' && isset($_REQUEST['newBandwidth']) ) { - $_COOKIE['zmBandwidth'] = validStr($_REQUEST['newBandwidth']); - setcookie( 'zmBandwidth', validStr($_REQUEST['newBandwidth']), time()+3600*24*30*12*10 ); - $refreshParent = true; -} - -// Event scope actions, view permissions only required -if ( canView('Events') ) { - - if ( isset( $_REQUEST['object'] ) and ( $_REQUEST['object'] == 'filter' ) ) { - if ( $action == 'addterm' ) { - $_REQUEST['filter'] = addFilterTerm( $_REQUEST['filter'], $_REQUEST['line'] ); - } elseif ( $action == 'delterm' ) { - $_REQUEST['filter'] = delFilterTerm( $_REQUEST['filter'], $_REQUEST['line'] ); - } else if ( canEdit( 'Events' ) ) { - if ( $action == 'delete' ) { - if ( ! empty($_REQUEST['Id']) ) { - dbQuery('DELETE FROM Filters WHERE Id=?', array($_REQUEST['Id'])); - } - } else if ( ( $action == 'Save' ) or ( $action == 'SaveAs' ) or ( $action == 'execute' ) ) { - # or ( $action == 'submit' ) ) { - - $sql = ''; - $_REQUEST['filter']['Query']['sort_field'] = validStr($_REQUEST['filter']['Query']['sort_field']); - $_REQUEST['filter']['Query']['sort_asc'] = validStr($_REQUEST['filter']['Query']['sort_asc']); - $_REQUEST['filter']['Query']['limit'] = validInt($_REQUEST['filter']['Query']['limit']); - if ( $action == 'execute' ) { - $tempFilterName = '_TempFilter'.time(); - $sql .= ' Name = \''.$tempFilterName.'\''; - } else { - $sql .= ' Name = '.dbEscape($_REQUEST['filter']['Name']); - } - $sql .= ', Query = '.dbEscape(jsonEncode($_REQUEST['filter']['Query'])); - $sql .= ', AutoArchive = '.(!empty($_REQUEST['filter']['AutoArchive']) ? 1 : 0); - $sql .= ', AutoVideo = '. ( !empty($_REQUEST['filter']['AutoVideo']) ? 1 : 0); - $sql .= ', AutoUpload = '. ( !empty($_REQUEST['filter']['AutoUpload']) ? 1 : 0); - $sql .= ', AutoEmail = '. ( !empty($_REQUEST['filter']['AutoEmail']) ? 1 : 0); - $sql .= ', AutoMessage = '. ( !empty($_REQUEST['filter']['AutoMessage']) ? 1 : 0); - $sql .= ', AutoExecute = '. ( !empty($_REQUEST['filter']['AutoExecute']) ? 1 : 0); - $sql .= ', AutoExecuteCmd = '.dbEscape($_REQUEST['filter']['AutoExecuteCmd']); - $sql .= ', AutoDelete = '. ( !empty($_REQUEST['filter']['AutoDelete']) ? 1 : 0); - if ( !empty($_REQUEST['filter']['AutoMove']) ? 1 : 0) { - $sql .= ', AutoMove = 1, AutoMoveTo='. validInt($_REQUEST['filter']['AutoMoveTo']); - } else { - $sql .= ', AutoMove = 0'; - } - $sql .= ', UpdateDiskSpace = '. ( !empty($_REQUEST['filter']['UpdateDiskSpace']) ? 1 : 0); - $sql .= ', Background = '. ( !empty($_REQUEST['filter']['Background']) ? 1 : 0); - $sql .= ', Concurrent = '. ( !empty($_REQUEST['filter']['Concurrent']) ? 1 : 0); - - if ( $_REQUEST['Id'] and ( $action == 'Save' ) ) { - dbQuery('UPDATE Filters SET ' . $sql. ' WHERE Id=?', array($_REQUEST['Id'])); - } else { - dbQuery('INSERT INTO Filters SET' . $sql); - $_REQUEST['Id'] = dbInsertId(); - } - if ( $action == 'execute' ) { - executeFilter( $tempFilterName ); - } - - } // end if save or execute - } // end if canEdit(Events) - return; - } // end if object == filter - else { - - // Event scope actions, edit permissions required - if ( canEdit('Events') ) { - if ( ($action == 'rename') && isset($_REQUEST['eventName']) && !empty($_REQUEST['eid']) ) { - dbQuery('UPDATE Events SET Name=? WHERE Id=?', array($_REQUEST['eventName'], $_REQUEST['eid'])); - } else if ( $action == 'eventdetail' ) { - if ( !empty($_REQUEST['eid']) ) { - dbQuery( 'UPDATE Events SET Cause=?, Notes=? WHERE Id=?', array( $_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $_REQUEST['eid'] ) ); - } else { - $dbConn->beginTransaction(); - foreach( getAffectedIds('markEid') as $markEid ) { - dbQuery( 'UPDATE Events SET Cause=?, Notes=? WHERE Id=?', array( $_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $markEid ) ); - } - $dbConn->commit(); - } - $refreshParent = true; - $closePopup = true; - } elseif ( $action == 'archive' || $action == 'unarchive' ) { - $archiveVal = ($action == 'archive')?1:0; - if ( !empty($_REQUEST['eid']) ) { - dbQuery('UPDATE Events SET Archived=? WHERE Id=?', array($archiveVal, $_REQUEST['eid'])); - } else { - $dbConn->beginTransaction(); - foreach( getAffectedIds( 'markEid' ) as $markEid ) { - dbQuery('UPDATE Events SET Archived=? WHERE Id=?', array($archiveVal, $markEid)); - } - $dbConn->commit(); - $refreshParent = true; - } - } elseif ( $action == 'delete' ) { - $dbConn->beginTransaction(); - foreach( getAffectedIds( 'markEid' ) as $markEid ) { - deleteEvent( $markEid ); - } - $dbConn->commit(); - $refreshParent = true; - } - } // end if canEdit(Events) - } // end if filter or something else -} // end canView(Events) - -// Monitor control actions, require a monitor id and control view permissions for that monitor -if ( !empty($_REQUEST['mid']) && canView( 'Control', $_REQUEST['mid'] ) ) { - require_once( 'control_functions.php' ); - require_once( 'Monitor.php' ); - $mid = validInt($_REQUEST['mid']); - if ( $action == 'control' ) { - $monitor = new Monitor( $mid ); - - $ctrlCommand = buildControlCommand( $monitor ); - sendControlCommand( $monitor->Id(), $ctrlCommand ); - } elseif ( $action == 'settings' ) { - $args = ' -m ' . escapeshellarg($mid); - $args .= ' -B' . escapeshellarg($_REQUEST['newBrightness']); - $args .= ' -C' . escapeshellarg($_REQUEST['newContrast']); - $args .= ' -H' . escapeshellarg($_REQUEST['newHue']); - $args .= ' -O' . escapeshellarg($_REQUEST['newColour']); - - $zmuCommand = getZmuCommand( $args ); - - $zmuOutput = exec( $zmuCommand ); - list( $brightness, $contrast, $hue, $colour ) = explode( ' ', $zmuOutput ); - dbQuery( 'UPDATE Monitors SET Brightness = ?, Contrast = ?, Hue = ?, Colour = ? WHERE Id = ?', array($brightness, $contrast, $hue, $colour, $mid)); - } -} - -// Control capability actions, require control edit permissions -if ( canEdit('Control') ) { - if ( $action == 'controlcap' ) { - require_once( 'Control.php' ); - $Control = new Control( !empty($_REQUEST['cid']) ? $_REQUEST['cid'] : null ); - - //$changes = getFormChanges( $control, $_REQUEST['newControl'], $types, $columns ); - $Control->save( $_REQUEST['newControl'] ); - $refreshParent = true; - $view = 'none'; - } elseif ( $action == 'delete' ) { - if ( isset($_REQUEST['markCids']) ) { - foreach( $_REQUEST['markCids'] as $markCid ) { - dbQuery( 'delete from Controls where Id = ?', array($markCid) ); - dbQuery( 'update Monitors set Controllable = 0, ControlId = 0 where ControlId = ?', array($markCid) ); - $refreshParent = true; - } - } - } // end if action -} // end if canEdit Controls - -if ( isset($_REQUEST['object']) and $_REQUEST['object'] == 'Monitor' ) { - if ( $action == 'save' ) { - foreach ( $_REQUEST['mids'] as $mid ) { - $mid = ValidInt( $mid ); - if ( ! canEdit('Monitors', $mid ) ) { - Warning("Cannot edit monitor $mid"); - continue; - } - $Monitor = new Monitor( $mid ); - if ( $Monitor->Type() != 'WebSite' ) { - $Monitor->zmaControl('stop'); - $Monitor->zmcControl('stop'); - } - $Monitor->save( $_REQUEST['newMonitor'] ); - if ($Monitor->Function() != 'None' && $Monitor->Type() != 'WebSite' ) { - $Monitor->zmcControl('start'); - if ( $Monitor->Enabled() ) { - $Monitor->zmaControl('start'); - } - } - - } // end foreach mid - $refreshParent = true; - } // end if action == save -} // end if object is Monitor - -// Monitor edit actions, require a monitor id and edit permissions for that monitor -if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) { - $mid = validInt($_REQUEST['mid']); - if ( $action == 'function' ) { - $monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id=?', NULL, array($mid) ); - - $newFunction = validStr($_REQUEST['newFunction']); - # Because we use a checkbox, it won't get passed in the request. So not being in _REQUEST means 0 - $newEnabled = ( !isset( $_REQUEST['newEnabled'] ) or $_REQUEST['newEnabled'] != '1' ) ? '0' : '1'; - $oldFunction = $monitor['Function']; - $oldEnabled = $monitor['Enabled']; - if ( $newFunction != $oldFunction || $newEnabled != $oldEnabled ) { - dbQuery( 'UPDATE Monitors SET Function=?, Enabled=? WHERE Id=?', array( $newFunction, $newEnabled, $mid ) ); - - $monitor['Function'] = $newFunction; - $monitor['Enabled'] = $newEnabled; - if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) { - $restart = ($oldFunction == 'None') || ($newFunction == 'None') || ($newEnabled != $oldEnabled); - zmaControl( $monitor, 'stop' ); - zmcControl( $monitor, $restart?'restart':'' ); - zmaControl( $monitor, 'start' ); - } - $refreshParent = true; - } - } elseif ( $action == 'zone' && isset( $_REQUEST['zid'] ) ) { - $zid = validInt($_REQUEST['zid']); - $monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id=?', NULL, array($mid) ); - - if ( !empty($zid) ) { - $zone = dbFetchOne( 'SELECT * FROM Zones WHERE MonitorId=? AND Id=?', NULL, array( $mid, $zid ) ); - } else { - $zone = array(); - } - - if ( $_REQUEST['newZone']['Units'] == 'Percent' ) { - $_REQUEST['newZone']['MinAlarmPixels'] = intval(($_REQUEST['newZone']['MinAlarmPixels']*$_REQUEST['newZone']['Area'])/100); - $_REQUEST['newZone']['MaxAlarmPixels'] = intval(($_REQUEST['newZone']['MaxAlarmPixels']*$_REQUEST['newZone']['Area'])/100); - if ( isset($_REQUEST['newZone']['MinFilterPixels']) ) - $_REQUEST['newZone']['MinFilterPixels'] = intval(($_REQUEST['newZone']['MinFilterPixels']*$_REQUEST['newZone']['Area'])/100); - if ( isset($_REQUEST['newZone']['MaxFilterPixels']) ) - $_REQUEST['newZone']['MaxFilterPixels'] = intval(($_REQUEST['newZone']['MaxFilterPixels']*$_REQUEST['newZone']['Area'])/100); - if ( isset($_REQUEST['newZone']['MinBlobPixels']) ) - $_REQUEST['newZone']['MinBlobPixels'] = intval(($_REQUEST['newZone']['MinBlobPixels']*$_REQUEST['newZone']['Area'])/100); - if ( isset($_REQUEST['newZone']['MaxBlobPixels']) ) - $_REQUEST['newZone']['MaxBlobPixels'] = intval(($_REQUEST['newZone']['MaxBlobPixels']*$_REQUEST['newZone']['Area'])/100); - } - - unset( $_REQUEST['newZone']['Points'] ); - $types = array(); - $changes = getFormChanges( $zone, $_REQUEST['newZone'], $types ); - - if ( count( $changes ) ) { - if ( $zid > 0 ) { - dbQuery( 'UPDATE Zones SET '.implode( ', ', $changes ).' WHERE MonitorId=? AND Id=?', array( $mid, $zid) ); - } else { - dbQuery( 'INSERT INTO Zones SET MonitorId=?, '.implode( ', ', $changes ), array( $mid ) ); - } - if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) { - if ( $_REQUEST['newZone']['Type'] == 'Privacy' ) { - zmaControl( $monitor, 'stop' ); - zmcControl( $monitor, 'restart' ); - zmaControl( $monitor, 'start' ); - } else { - zmaControl( $mid, 'restart' ); - } - } - if ( $_REQUEST['newZone']['Type'] == 'Privacy' && $monitor['Controllable'] ) { - require_once( 'control_functions.php' ); - sendControlCommand( $mid, 'quit' ); - } - $refreshParent = true; - } - $view = 'none'; - } elseif ( $action == 'plugin' && isset($_REQUEST['pl'])) { - $sql='SELECT * FROM PluginsConfig WHERE MonitorId=? AND ZoneId=? AND pluginName=?'; - $pconfs=dbFetchAll( $sql, NULL, array( $mid, $_REQUEST['zid'], $_REQUEST['pl'] ) ); - $changes=0; - foreach( $pconfs as $pconf ) { - $value=$_REQUEST['pluginOpt'][$pconf['Name']]; - if(array_key_exists($pconf['Name'], $_REQUEST['pluginOpt']) && ($pconf['Value']!=$value)) { - dbQuery('UPDATE PluginsConfig SET Value=? WHERE id=?', array( $value, $pconf['Id'] ) ); - $changes++; - } - } - if($changes>0) { - if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) { - zmaControl( $mid, 'restart' ); - } - $refreshParent = true; - } - $view = 'none'; - } elseif ( $action == 'sequence' && isset($_REQUEST['smid']) ) { - $smid = validInt($_REQUEST['smid']); - $monitor = dbFetchOne( 'select * from Monitors where Id = ?', NULL, array($mid) ); - $smonitor = dbFetchOne( 'select * from Monitors where Id = ?', NULL, array($smid) ); - - dbQuery( 'update Monitors set Sequence=? where Id=?', array( $smonitor['Sequence'], $monitor['Id'] ) ); - dbQuery( 'update Monitors set Sequence=? WHERE Id=?', array( $monitor['Sequence'], $smonitor['Id'] ) ); - - $refreshParent = true; - fixSequences(); - } elseif ( $action == 'delete' ) { - if ( isset($_REQUEST['markZids']) ) { - $deletedZid = 0; - foreach( $_REQUEST['markZids'] as $markZid ) { - $zone = dbFetchOne( 'select * from Zones where Id=?', NULL, array($markZid) ); - dbQuery( 'delete from Zones WHERE MonitorId=? AND Id=?', array( $mid, $markZid) ); - $deletedZid = 1; - } - if ( $deletedZid ) { - if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) { - if ( $zone['Type'] == 'Privacy' ) { - zmaControl( $mid, 'stop' ); - zmcControl( $mid, 'restart' ); - zmaControl( $mid, 'start' ); - } else { - zmaControl( $mid, 'restart' ); - } - } // end if daemonCheck() - $refreshParent = true; - } // end if deletedzid - } // end if isset($_REQUEST['markZids']) - } // end if action -} // end if $mid and canEdit($mid) - -// Monitor edit actions, monitor id derived, require edit permissions for that monitor -if ( canEdit( 'Monitors' ) ) { - if ( $action == 'monitor' ) { - $mid = 0; - if ( !empty($_REQUEST['mid']) ) { - $mid = validInt($_REQUEST['mid']); - $monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id=?', NULL, array($mid) ); - - if ( ZM_OPT_X10 ) { - $x10Monitor = dbFetchOne( 'SELECT * FROM TriggersX10 WHERE MonitorId=?', NULL, array($mid) ); - if ( !$x10Monitor ) - $x10Monitor = array(); - } - } else { - $monitor = array(); - if ( ZM_OPT_X10 ) { - $x10Monitor = array(); - } - } - $Monitor = new Monitor($monitor); - - // Define a field type for anything that's not simple text equivalent - $types = array( - 'Triggers' => 'set', - 'Controllable' => 'toggle', - 'TrackMotion' => 'toggle', - 'Enabled' => 'toggle', - 'DoNativeMotDet' => 'toggle', - 'Exif' => 'toggle', - 'RTSPDescribe' => 'toggle', - 'RecordAudio' => 'toggle', - 'Method' => 'raw', - ); - - if ( $_REQUEST['newMonitor']['ServerId'] == 'auto' ) { - $_REQUEST['newMonitor']['ServerId'] = dbFetchOne('SELECT Id FROM Servers WHERE Status=\'Running\' ORDER BY FreeMem DESC, CpuLoad ASC LIMIT 1', 'Id'); - Logger::Debug("Auto selecting server: Got " . $_REQUEST['newMonitor']['ServerId'] ); - if ( ( ! $_REQUEST['newMonitor'] ) and defined('ZM_SERVER_ID') ) { - $_REQUEST['newMonitor']['ServerId'] = ZM_SERVER_ID; - Logger::Debug("Auto selecting server to " . ZM_SERVER_ID); - } - } - - $columns = getTableColumns('Monitors'); - $changes = getFormChanges($monitor, $_REQUEST['newMonitor'], $types, $columns); - - if ( count( $changes ) ) { - if ( $mid ) { - - # If we change anything that changes the shared mem size, zma can complain. So let's stop first. - if ( $monitor['Type'] != 'WebSite' ) { - zmaControl($monitor, 'stop'); - zmcControl($monitor, 'stop'); - } - dbQuery( 'UPDATE Monitors SET '.implode( ', ', $changes ).' WHERE Id=?', array($mid) ); - // Groups will be added below - if ( isset($changes['Name']) or isset($changes['StorageId']) ) { - $OldStorage = new Storage( $monitor['StorageId'] ); - $saferOldName = basename( $monitor['Name'] ); - if ( file_exists( $OldStorage->Path().'/'.$saferOldName ) ) - unlink( $OldStorage->Path().'/'.$saferOldName ); - - $NewStorage = new Storage( $_REQUEST['newMonitor']['StorageId'] ); - if ( ! file_exists( $NewStorage->Path().'/'.$mid ) ) - mkdir( $NewStorage->Path().'/'.$mid, 0755 ); - $saferNewName = basename( $_REQUEST['newMonitor']['Name'] ); - symlink( $mid, $NewStorage->Path().'/'.$saferNewName ); - } - if ( isset($changes['Width']) || isset($changes['Height']) ) { - $newW = $_REQUEST['newMonitor']['Width']; - $newH = $_REQUEST['newMonitor']['Height']; - $newA = $newW * $newH; - $oldW = $monitor['Width']; - $oldH = $monitor['Height']; - $oldA = $oldW * $oldH; - - $zones = dbFetchAll( 'SELECT * FROM Zones WHERE MonitorId=?', NULL, array($mid) ); - foreach ( $zones as $zone ) { - $newZone = $zone; - $points = coordsToPoints( $zone['Coords'] ); - for ( $i = 0; $i < count($points); $i++ ) { - $points[$i]['x'] = intval(($points[$i]['x']*($newW-1))/($oldW-1)); - $points[$i]['y'] = intval(($points[$i]['y']*($newH-1))/($oldH-1)); - } - $newZone['Coords'] = pointsToCoords( $points ); - $newZone['Area'] = intval(round(($zone['Area']*$newA)/$oldA)); - $newZone['MinAlarmPixels'] = intval(round(($newZone['MinAlarmPixels']*$newA)/$oldA)); - $newZone['MaxAlarmPixels'] = intval(round(($newZone['MaxAlarmPixels']*$newA)/$oldA)); - $newZone['MinFilterPixels'] = intval(round(($newZone['MinFilterPixels']*$newA)/$oldA)); - $newZone['MaxFilterPixels'] = intval(round(($newZone['MaxFilterPixels']*$newA)/$oldA)); - $newZone['MinBlobPixels'] = intval(round(($newZone['MinBlobPixels']*$newA)/$oldA)); - $newZone['MaxBlobPixels'] = intval(round(($newZone['MaxBlobPixels']*$newA)/$oldA)); - - $changes = getFormChanges( $zone, $newZone, $types ); - - if ( count( $changes ) ) { - dbQuery( 'update Zones set '.implode( ', ', $changes ).' WHERE MonitorId=? AND Id=?', array( $mid, $zone['Id'] ) ); - } - } - } - $restart = true; - } else if ( ! $user['MonitorIds'] ) { // Can only create new monitors if we are not restricted to specific monitors -# FIXME This is actually a race condition. Should lock the table. - $maxSeq = dbFetchOne('SELECT MAX(Sequence) AS MaxSequence FROM Monitors', 'MaxSequence'); - $changes[] = 'Sequence = '.($maxSeq+1); - - if ( dbQuery( 'INSERT INTO Monitors SET '.implode( ', ', $changes ) ) ) { - $mid = dbInsertId(); - $zoneArea = $_REQUEST['newMonitor']['Width'] * $_REQUEST['newMonitor']['Height']; - dbQuery( "insert into Zones set MonitorId = ?, Name = 'All', Type = 'Active', Units = 'Percent', NumCoords = 4, Coords = ?, Area=?, AlarmRGB = 0xff0000, CheckMethod = 'Blobs', MinPixelThreshold = 25, MinAlarmPixels=?, MaxAlarmPixels=?, FilterX = 3, FilterY = 3, MinFilterPixels=?, MaxFilterPixels=?, MinBlobPixels=?, MinBlobs = 1", array( $mid, sprintf( "%d,%d %d,%d %d,%d %d,%d", 0, 0, $_REQUEST['newMonitor']['Width']-1, 0, $_REQUEST['newMonitor']['Width']-1, $_REQUEST['newMonitor']['Height']-1, 0, $_REQUEST['newMonitor']['Height']-1 ), $zoneArea, intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*2)/100) ) ); - //$view = 'none'; - $Storage = new Storage( $_REQUEST['newMonitor']['StorageId'] ); - mkdir( $Storage->Path().'/'.$mid, 0755 ); - $saferName = basename($_REQUEST['newMonitor']['Name']); - symlink( $mid, $Storage->Path().'/'.$saferName ); - - } else { - Error("Error saving new Monitor."); - return; - } - } else { - Error("Users with Monitors restrictions cannot create new monitors."); - return; - } - - $restart = true; - } else { - Logger::Debug("No action due to no changes to Monitor"); - } # end if count(changes) - - if ( - ( !isset($_POST['newMonitor']['GroupIds']) ) - or - ( count($_POST['newMonitor']['GroupIds']) != count($Monitor->GroupIds()) ) - or - array_diff($_POST['newMonitor']['GroupIds'], $Monitor->GroupIds()) - ) { - if ( $Monitor->Id() ) - dbQuery('DELETE FROM Groups_Monitors WHERE MonitorId=?', array($mid)); - - if ( isset($_POST['newMonitor']['GroupIds']) ) { - foreach ( $_POST['newMonitor']['GroupIds'] as $group_id ) { - dbQuery('INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($group_id, $mid)); - } - } - } // end if there has been a change of groups - - if ( ZM_OPT_X10 ) { - $x10Changes = getFormChanges( $x10Monitor, $_REQUEST['newX10Monitor'] ); - - if ( count( $x10Changes ) ) { - if ( $x10Monitor && isset($_REQUEST['newX10Monitor']) ) { - dbQuery( 'update TriggersX10 set '.implode( ', ', $x10Changes ).' where MonitorId=?', array($mid) ); - } elseif ( !$user['MonitorIds'] ) { - if ( !$x10Monitor ) { - dbQuery( 'insert into TriggersX10 set MonitorId = ?, '.implode( ', ', $x10Changes ), array( $mid ) ); - } else { - dbQuery( 'delete from TriggersX10 where MonitorId = ?', array($mid) ); - } - } - $restart = true; - } - } - - if ( $restart ) { - - $new_monitor = new Monitor($mid); - //fixDevices(); - - if ( $monitor['Type'] != 'WebSite' ) { - $new_monitor->zmcControl('start'); - $new_monitor->zmaControl('start'); - } - - if ( $new_monitor->Controllable() ) { - require_once( 'control_functions.php' ); - sendControlCommand( $mid, 'quit' ); - } - // really should thump zmwatch and maybe zmtrigger too. - //daemonControl( 'restart', 'zmwatch.pl' ); - $refreshParent = true; - } // end if restart - $view = 'none'; - } elseif ( $action == 'delete' ) { - if ( isset($_REQUEST['markMids']) && !$user['MonitorIds'] ) { - require_once( 'Monitor.php' ); - foreach( $_REQUEST['markMids'] as $markMid ) { - if ( canEdit('Monitors', $markMid) ) { - // This could be faster as a select all - if ( $monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id = ?', NULL, array($markMid) ) ) { - $Monitor = new Monitor($monitor); - $Monitor->delete(); - } // end if monitor found in db - } // end if canedit this monitor - } // end foreach monitor in MarkMid - } // markMids is set and we aren't limited to specific monitors - } // end if action == Delete -} - -// Device view actions -if ( canEdit( 'Devices' ) ) { - if ( $action == 'device' ) { - if ( !empty($_REQUEST['command']) ) { - setDeviceStatusX10( $_REQUEST['key'], $_REQUEST['command'] ); - } elseif ( isset( $_REQUEST['newDevice'] ) ) { - if ( isset($_REQUEST['did']) ) { - dbQuery( 'update Devices set Name=?, KeyString=? where Id=?', array($_REQUEST['newDevice']['Name'], $_REQUEST['newDevice']['KeyString'], $_REQUEST['did']) ); - } else { - dbQuery( 'insert into Devices set Name=?, KeyString=?', array( $_REQUEST['newDevice']['Name'], $_REQUEST['newDevice']['KeyString'] ) ); - } - $refreshParent = true; - $view = 'none'; - } - } elseif ( $action == 'delete' ) { - if ( isset($_REQUEST['markDids']) ) { - foreach( $_REQUEST['markDids'] as $markDid ) { - dbQuery( 'delete from Devices where Id=?', array($markDid) ); - $refreshParent = true; - } - } - } // end if action -} // end if canedit devices - -// Group view actions -if ( canView( 'Groups' ) && $action == 'setgroup' ) { - if ( !empty($_REQUEST['gid']) ) { - setcookie( 'zmGroup', validInt($_REQUEST['gid']), time()+3600*24*30*12*10 ); - } else { - setcookie( 'zmGroup', '', time()-3600*24*2 ); - } - $refreshParent = true; -} - -// Group edit actions -# Should probably verify that each monitor id is a valid monitor, that we have access to. However at the moment, you have to have System permissions to do this -if ( canEdit( 'Groups' ) ) { - if ( $action == 'group' ) { - $monitors = empty( $_POST['newGroup']['MonitorIds'] ) ? '' : implode(',', $_POST['newGroup']['MonitorIds']); - $group_id = null; - if ( !empty($_POST['gid']) ) { - $group_id = $_POST['gid']; - dbQuery( 'UPDATE Groups SET Name=?, ParentId=? WHERE Id=?', - array($_POST['newGroup']['Name'], ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), $group_id) ); - dbQuery( 'DELETE FROM Groups_Monitors WHERE GroupId=?', array($group_id) ); - } else { - dbQuery( 'INSERT INTO Groups (Name,ParentId) VALUES (?,?)', - array( $_POST['newGroup']['Name'], ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ) ) ); - $group_id=dbInsertId(); - } - if ( $group_id ) { - foreach ( $_POST['newGroup']['MonitorIds'] as $mid ) { - dbQuery( 'INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($group_id, $mid) ); - } - } - $view = 'none'; - $refreshParent = true; - } else if ( $action == 'delete' ) { - if ( !empty($_REQUEST['gid']) ) { - if ( is_array( $_REQUEST['gid'] ) ) { - foreach( $_REQUEST['gid'] as $gid ) { - $Group = new Group( $gid ); - $Group->delete(); - } - } else { - $Group = new Group( $_REQUEST['gid'] ); - $Group->delete(); - } - } - $refreshParent = true; - } # end if action -} // end if can edit groups - -// System edit actions -if ( canEdit( 'System' ) ) { - if ( isset( $_REQUEST['object'] ) ) { - if ( $_REQUEST['object'] == 'MontageLayout' ) { - require_once('MontageLayout.php'); - if ( $action == 'Save' ) { - $Layout = null; - if ( $_REQUEST['Name'] != '' ) { - $Layout = new MontageLayout(); - $Layout->Name( $_REQUEST['Name'] ); - } else { - $Layout = new MontageLayout( $_REQUEST['zmMontageLayout'] ); - } - $Layout->Positions( $_REQUEST['Positions'] ); - $Layout->save(); - session_start(); - $_SESSION['zmMontageLayout'] = $Layout->Id(); - setcookie('zmMontageLayout', $Layout->Id(), 1 ); - session_write_close(); - $redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=montagereview'; - } // end if save - - } else if ( $_REQUEST['object'] == 'server' ) { - - if ( $action == 'Save' ) { - if ( !empty($_REQUEST['id']) ) - $dbServer = dbFetchOne( 'SELECT * FROM Servers WHERE Id=?', NULL, array($_REQUEST['id']) ); - else - $dbServer = array(); - - $types = array(); - $changes = getFormChanges( $dbServer, $_REQUEST['newServer'], $types ); - - if ( count( $changes ) ) { - if ( !empty($_REQUEST['id']) ) { - dbQuery( 'UPDATE Servers SET '.implode( ', ', $changes ).' WHERE Id = ?', array($_REQUEST['id']) ); - } else { - dbQuery( 'INSERT INTO Servers set '.implode( ', ', $changes ) ); - } - $refreshParent = true; - } - $view = 'none'; - } else if ( $action == 'delete' ) { - if ( !empty($_REQUEST['markIds']) ) { - foreach( $_REQUEST['markIds'] as $Id ) - dbQuery( 'DELETE FROM Servers WHERE Id=?', array($Id) ); - } - $refreshParent = true; - } else { - Error( "Unknown action $action in saving Server" ); - } - } else if ( $_REQUEST['object'] == 'storage' ) { - if ( $action == 'Save' ) { - if ( !empty($_REQUEST['id']) ) - $dbStorage = dbFetchOne( 'SELECT * FROM Storage WHERE Id=?', NULL, array($_REQUEST['id']) ); - else - $dbStorage = array(); - - $types = array(); - $changes = getFormChanges( $dbStorage, $_REQUEST['newStorage'], $types ); - - if ( count( $changes ) ) { - if ( !empty($_REQUEST['id']) ) { - dbQuery( 'UPDATE Storage SET '.implode( ', ', $changes ).' WHERE Id = ?', array($_REQUEST['id']) ); - } else { - dbQuery( 'INSERT INTO Storage set '.implode( ', ', $changes ) ); - } - $refreshParent = true; - } - $view = 'none'; - } else if ( $action == 'delete' ) { - if ( !empty($_REQUEST['markIds']) ) { - foreach( $_REQUEST['markIds'] as $Id ) - dbQuery( 'DELETE FROM Storage WHERE Id=?', array($Id) ); - } - $refreshParent = true; - } else { - Error( "Unknown action $action in saving Storage" ); - } - } # end if isset($_REQUEST['object'] ) - - } else if ( $action == 'version' && isset($_REQUEST['option']) ) { - $option = $_REQUEST['option']; - switch( $option ) { - case 'go' : - { - // Ignore this, the caller will open the page itself - break; - } - case 'ignore' : - { - dbQuery( "update Config set Value = '".ZM_DYN_LAST_VERSION."' where Name = 'ZM_DYN_CURR_VERSION'" ); - break; - } - case 'hour' : - case 'day' : - case 'week' : - { - $nextReminder = time(); - if ( $option == 'hour' ) { - $nextReminder += 60*60; - } elseif ( $option == 'day' ) { - $nextReminder += 24*60*60; - } elseif ( $option == 'week' ) { - $nextReminder += 7*24*60*60; - } - dbQuery( "update Config set Value = '".$nextReminder."' where Name = 'ZM_DYN_NEXT_REMINDER'" ); - break; - } - case 'never' : - { - dbQuery( "update Config set Value = '0' where Name = 'ZM_CHECK_FOR_UPDATES'" ); - break; - } - } - } - if ( $action == 'donate' && isset($_REQUEST['option']) ) { - $option = $_REQUEST['option']; - switch( $option ) { - case 'go' : - { - // Ignore this, the caller will open the page itself - break; - } - case 'hour' : - case 'day' : - case 'week' : - case 'month' : - { - $nextReminder = time(); - if ( $option == 'hour' ) { - $nextReminder += 60*60; - } elseif ( $option == 'day' ) { - $nextReminder += 24*60*60; - } elseif ( $option == 'week' ) { - $nextReminder += 7*24*60*60; - } elseif ( $option == 'month' ) { - $nextReminder += 30*24*60*60; - } - dbQuery( "update Config set Value = '".$nextReminder."' where Name = 'ZM_DYN_DONATE_REMINDER_TIME'" ); - break; - } - case 'never' : - case 'already' : - { - dbQuery( "update Config set Value = '0' where Name = 'ZM_DYN_SHOW_DONATE_REMINDER'" ); - break; - } - } // end switch option - } - if ( $action == 'options' && isset($_REQUEST['tab']) ) { - $configCat = $configCats[$_REQUEST['tab']]; - $changed = false; - foreach ( $configCat as $name=>$value ) { - unset( $newValue ); - if ( $value['Type'] == 'boolean' && empty($_REQUEST['newConfig'][$name]) ) { - $newValue = 0; - } else if ( isset($_REQUEST['newConfig'][$name]) ) { - $newValue = preg_replace( "/\r\n/", "\n", stripslashes( $_REQUEST['newConfig'][$name] ) ); - } - - if ( isset($newValue) && ($newValue != $value['Value']) ) { - dbQuery( 'UPDATE Config SET Value=? WHERE Name=?', array( $newValue, $name ) ); - $changed = true; - } - } - if ( $changed ) { - switch( $_REQUEST['tab'] ) { - case 'system' : - case 'config' : - $restartWarning = true; - break; - case 'web' : - case 'tools' : - break; - case 'logging' : - case 'network' : - case 'mail' : - case 'upload' : - $restartWarning = true; - break; - case 'highband' : - case 'medband' : - case 'lowband' : - break; - } - $redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=options&tab='.$_REQUEST['tab']; - } - loadConfig( false ); - } elseif ( $action == 'user' ) { - if ( !empty($_REQUEST['uid']) ) - $dbUser = dbFetchOne( "SELECT * FROM Users WHERE Id=?", NULL, array($_REQUEST['uid']) ); - else - $dbUser = array(); - - $types = array(); - $changes = getFormChanges( $dbUser, $_REQUEST['newUser'], $types ); - - if ( $_REQUEST['newUser']['Password'] ) - $changes['Password'] = 'Password = password('.dbEscape($_REQUEST['newUser']['Password']).')'; - else - unset( $changes['Password'] ); - - if ( count( $changes ) ) { - if ( !empty($_REQUEST['uid']) ) { - dbQuery( 'update Users set '.implode( ', ', $changes ).' where Id = ?', array($_REQUEST['uid']) ); - # If we are updating the logged in user, then update our session user data. - if ( $user and ( $dbUser['Username'] == $user['Username'] ) ) - userLogin( $dbUser['Username'], $dbUser['Password'] ); - } else { - dbQuery( 'insert into Users set '.implode( ', ', $changes ) ); - } - $refreshParent = true; - } - $view = 'none'; - } elseif ( $action == 'state' ) { - if ( !empty($_REQUEST['runState']) ) { - //if ( $cookies ) session_write_close(); - packageControl( $_REQUEST['runState'] ); - $refreshParent = true; - } - } elseif ( $action == 'save' ) { - if ( !empty($_REQUEST['runState']) || !empty($_REQUEST['newState']) ) { - $sql = 'SELECT Id,Function,Enabled FROM Monitors ORDER BY Id'; - $definitions = array(); - foreach( dbFetchAll( $sql ) as $monitor ) - { - $definitions[] = $monitor['Id'].':'.$monitor['Function'].':'.$monitor['Enabled']; - } - $definition = join( ',', $definitions ); - if ( $_REQUEST['newState'] ) - $_REQUEST['runState'] = $_REQUEST['newState']; - dbQuery( 'replace into States set Name=?, Definition=?', array( $_REQUEST['runState'],$definition) ); - } - } elseif ( $action == 'delete' ) { - if ( isset($_REQUEST['runState']) ) - dbQuery( 'delete from States where Name=?', array($_REQUEST['runState']) ); - - if ( isset($_REQUEST['markUids']) ) { - foreach( $_REQUEST['markUids'] as $markUid ) - dbQuery( 'delete from Users where Id = ?', array($markUid) ); - if ( $markUid == $user['Id'] ) - userLogout(); - } - } -} else { - if ( ZM_USER_SELF_EDIT && $action == 'user' ) { - $uid = $user['Id']; - - $dbUser = dbFetchOne( 'SELECT Id, Password, Language FROM Users WHERE Id = ?', NULL, array($uid) ); - - $types = array(); - $changes = getFormChanges( $dbUser, $_REQUEST['newUser'], $types ); - - if ( !empty($_REQUEST['newUser']['Password']) ) - $changes['Password'] = 'Password = password('.dbEscape($_REQUEST['newUser']['Password']).')'; - else - unset( $changes['Password'] ); - if ( count( $changes ) ) { - dbQuery( 'update Users set '.implode( ', ', $changes ).' where Id=?', array($uid) ); - $refreshParent = true; - } - $view = 'none'; - } -} - -if ( $action == 'reset' ) { - session_start(); - $_SESSION['zmEventResetTime'] = strftime( STRF_FMT_DATETIME_DB ); - setcookie( 'zmEventResetTime', $_SESSION['zmEventResetTime'], time()+3600*24*30*12*10 ); - session_write_close(); -} - -?> diff --git a/web/includes/actions/bandwidth.php b/web/includes/actions/bandwidth.php new file mode 100644 index 000000000..c5fafa1c9 --- /dev/null +++ b/web/includes/actions/bandwidth.php @@ -0,0 +1,29 @@ + diff --git a/web/includes/actions/console.php b/web/includes/actions/console.php new file mode 100644 index 000000000..6e0dfbb6c --- /dev/null +++ b/web/includes/actions/console.php @@ -0,0 +1,40 @@ +delete(); + } // end if monitor found in db + } // end if canedit this monitor + } // end foreach monitor in MarkMid + } // markMids is set and we aren't limited to specific monitors +} // end if action == Delete +?> diff --git a/web/includes/actions/control.php b/web/includes/actions/control.php new file mode 100644 index 000000000..5c996e636 --- /dev/null +++ b/web/includes/actions/control.php @@ -0,0 +1,41 @@ +sendControlCommand($ctrlCommand); + $view = 'none'; +} +?> diff --git a/web/includes/actions/controlcap.php b/web/includes/actions/controlcap.php new file mode 100644 index 000000000..ad4985f11 --- /dev/null +++ b/web/includes/actions/controlcap.php @@ -0,0 +1,95 @@ + 0, + 'CanSleep' => 0, + 'CanReset' => 0, + 'CanReboot' => 0, + 'CanMove' => 0, + 'CanMoveDiag' => 0, + 'CanMoveMap' => 0, + 'CanMoveRel' => 0, + 'CanMoveAbs' => 0, + 'CanMoveCon' => 0, + 'CanPan' => 0, + 'HasPanSpeed' => 0, + 'HasTurboPan' => 0, + 'CanTilt' => 0, + 'HasTiltSpeed' => 0, + 'HasTurboTilt' => 0, + 'CanZoom' => 0, + 'CanZoomRel' => 0, + 'CanZoomAbs' => 0, + 'CanZoomCon' => 0, + 'HasZoomSpeed' => 0, + 'CanFocus' => 0, + 'CanAutoFocus' => 0, + 'CanFocusRel' => 0, + 'CanFocusAbs' => 0, + 'CanFocusCon' => 0, + 'HasFocusSpeed' => 0, + 'CanGain' => 0, + 'CanAutoGain' => 0, + 'CanGainRel' => 0, + 'CanGainAbs' => 0, + 'CanGainCon' => 0, + 'HasGainSpeed' => 0, + 'CanWhite' => 0, + 'CanAutoWhite' => 0, + 'CanWhiteRel' => 0, + 'CanWhiteAbs' => 0, + 'CanWhiteCon' => 0, + 'HasWhiteSpeed' => 0, + 'CanIris' => 0, + 'CanAutoIris' => 0, + 'CanIrisRel' => 0, + 'CanIrisAbs' => 0, + 'CanIrisCon' => 0, + 'HasIrisSpeed' => 0, + 'HasPresets' => 0, + 'HasHomePreset' => 0, + 'CanSetPresets' => 0, + ); + + # Checkboxes don't return an element in the POST data, so won't be present in newControl. + # So force a value for these fields + foreach ( $field_defaults as $field => $value ) { + if ( ! (isset($_REQUEST['newControl'][$field]) and $_REQUEST['newControl'][$field]) ) { + $_REQUEST['newControl'][$field] = $value; + } + } # end foreach type + + //$changes = getFormChanges( $control, $_REQUEST['newControl'], $types, $columns ); + $Control->save($_REQUEST['newControl']); + $refreshParent = true; + $view = 'none'; +} // end if action +?> diff --git a/web/includes/actions/controlcaps.php b/web/includes/actions/controlcaps.php new file mode 100644 index 000000000..3e5310d19 --- /dev/null +++ b/web/includes/actions/controlcaps.php @@ -0,0 +1,36 @@ + diff --git a/web/includes/actions/device.php b/web/includes/actions/device.php new file mode 100644 index 000000000..c1a882d89 --- /dev/null +++ b/web/includes/actions/device.php @@ -0,0 +1,45 @@ + diff --git a/web/includes/actions/devices.php b/web/includes/actions/devices.php new file mode 100644 index 000000000..01c562370 --- /dev/null +++ b/web/includes/actions/devices.php @@ -0,0 +1,35 @@ + diff --git a/web/includes/actions/donate.php b/web/includes/actions/donate.php new file mode 100644 index 000000000..b8659b269 --- /dev/null +++ b/web/includes/actions/donate.php @@ -0,0 +1,54 @@ + diff --git a/web/includes/actions/event.php b/web/includes/actions/event.php new file mode 100644 index 000000000..a3c6d56c3 --- /dev/null +++ b/web/includes/actions/event.php @@ -0,0 +1,51 @@ + diff --git a/web/includes/actions/eventdetail.php b/web/includes/actions/eventdetail.php new file mode 100644 index 000000000..20bd084ee --- /dev/null +++ b/web/includes/actions/eventdetail.php @@ -0,0 +1,47 @@ +beginTransaction(); + foreach ( $_REQUEST['markEids'] as $markEid ) { + dbQuery('UPDATE Events SET Cause=?, Notes=? WHERE Id=?', + array( + $_REQUEST['newEvent']['Cause'], + $_REQUEST['newEvent']['Notes'], + $markEid + ) + ); + } + $dbConn->commit(); + $refreshParent = true; + $closePopup = true; +} +?> diff --git a/web/includes/actions/events.php b/web/includes/actions/events.php new file mode 100644 index 000000000..abea69b60 --- /dev/null +++ b/web/includes/actions/events.php @@ -0,0 +1,52 @@ +beginTransaction(); + foreach( getAffectedIds('eids') as $markEid ) { + dbQuery('UPDATE Events SET Archived=? WHERE Id=?', array(1, $markEid)); + } + $dbConn->commit(); + $refreshParent = true; +} else if ( $action == 'unarchive' ) { + $dbConn->beginTransaction(); + foreach( getAffectedIds('eids') as $markEid ) { + dbQuery('UPDATE Events SET Archived=? WHERE Id=?', array(0, $markEid)); + } + $dbConn->commit(); + $refreshParent = true; +} else if ( $action == 'delete' ) { + foreach ( getAffectedIds('eids') as $markEid ) { + deleteEvent($markEid); + } + $refreshParent = true; +} +?> diff --git a/web/includes/actions/filter.php b/web/includes/actions/filter.php new file mode 100644 index 000000000..ac2435302 --- /dev/null +++ b/web/includes/actions/filter.php @@ -0,0 +1,112 @@ +Background() ) { + $filter->control('stop'); + } + $filter->delete(); + + } else { + ZM\Error('No filter id passed when deleting'); + } + } else if ( ( $action == 'Save' ) or ( $action == 'SaveAs' ) or ( $action == 'execute' ) ) { + + $sql = ''; + $_REQUEST['filter']['Query']['sort_field'] = validStr($_REQUEST['filter']['Query']['sort_field']); + $_REQUEST['filter']['Query']['sort_asc'] = validStr($_REQUEST['filter']['Query']['sort_asc']); + $_REQUEST['filter']['Query']['limit'] = validInt($_REQUEST['filter']['Query']['limit']); + + $_REQUEST['filter']['AutoCopy'] = empty($_REQUEST['filter']['AutoCopy']) ? 0 : 1; + $_REQUEST['filter']['AutoMove'] = empty($_REQUEST['filter']['AutoMove']) ? 0 : 1; + $_REQUEST['filter']['AutoArchive'] = empty($_REQUEST['filter']['AutoArchive']) ? 0 : 1; + $_REQUEST['filter']['AutoVideo'] = empty($_REQUEST['filter']['AutoVideo']) ? 0 : 1; + $_REQUEST['filter']['AutoUpload'] = empty($_REQUEST['filter']['AutoUpload']) ? 0 : 1; + $_REQUEST['filter']['AutoEmail'] = empty($_REQUEST['filter']['AutoEmail']) ? 0 : 1; + $_REQUEST['filter']['AutoMessage'] = empty($_REQUEST['filter']['AutoMessage']) ? 0 : 1; + $_REQUEST['filter']['AutoExecute'] = empty($_REQUEST['filter']['AutoExecute']) ? 0 : 1; + $_REQUEST['filter']['AutoDelete'] = empty($_REQUEST['filter']['AutoDelete']) ? 0 : 1; + $_REQUEST['filter']['UpdateDiskSpace'] = empty($_REQUEST['filter']['UpdateDiskSpace']) ? 0 : 1; + $_REQUEST['filter']['Background'] = empty($_REQUEST['filter']['Background']) ? 0 : 1; + $_REQUEST['filter']['Concurrent'] = empty($_REQUEST['filter']['Concurrent']) ? 0 : 1; + $changes = $filter->changes($_REQUEST['filter']); + ZM\Logger::Debug('Changes: ' . print_r($changes,true)); + + if ( $_REQUEST['Id'] and ( $action == 'Save' ) ) { + if ( $filter->Background() ) + $filter->control('stop'); + $filter->save($changes); + } else { + + if ( $action == 'execute' ) { + if ( count($changes) ) { + $filter->Name('_TempFilter'.time()); + $filter->Id(null); + } + } else if ( $action == 'SaveAs' ) { + $filter->Id(null); + } + $filter->save($changes); + + // We update the request id so that the newly saved filter is auto-selected + $_REQUEST['Id'] = $filter->Id(); + } + if ( $filter->Background() ) + $filter->control('start'); + + if ( $action == 'execute' ) { + $filter->execute(); + if ( count($changes) ) + $filter->delete(); + + $view = 'events'; + } + + } else if ( $action == 'control' ) { + if ( $_REQUEST['command'] == 'start' + or $_REQUEST['command'] == 'stop' + or $_REQUEST['command'] == 'restart' + ) { + $filter->control($_REQUEST['command'], $_REQUEST['ServerId']); + } else { + ZM\Error('Invalid command for filter ('.$_REQUEST['command'].')'); + } + } // end if save or execute + } // end if canEdit(Events) +} // end if object == filter + +?> diff --git a/web/includes/actions/function.php b/web/includes/actions/function.php new file mode 100644 index 000000000..00e4e21f7 --- /dev/null +++ b/web/includes/actions/function.php @@ -0,0 +1,60 @@ + diff --git a/web/includes/actions/group.php b/web/includes/actions/group.php new file mode 100644 index 000000000..9aa988540 --- /dev/null +++ b/web/includes/actions/group.php @@ -0,0 +1,62 @@ + diff --git a/web/includes/actions/groups.php b/web/includes/actions/groups.php new file mode 100644 index 000000000..8e4522187 --- /dev/null +++ b/web/includes/actions/groups.php @@ -0,0 +1,49 @@ +$_REQUEST['gid'])) as $Group ) { + $Group->delete(); + } + } + $redirect = '?view=groups'; + $refreshParent = true; +} # end if action +?> diff --git a/web/includes/actions/login.php b/web/includes/actions/login.php new file mode 100644 index 000000000..68ca4e604 --- /dev/null +++ b/web/includes/actions/login.php @@ -0,0 +1,110 @@ + ZM_OPT_GOOG_RECAPTCHA_SECRETKEY, + 'response' => $_REQUEST['g-recaptcha-response'], + 'remoteip' => $_SERVER['REMOTE_ADDR'] + ); + $res = do_post_request($url, http_build_query($fields)); + $responseData = json_decode($res, true); + // credit: https://github.com/google/recaptcha/blob/master/src/ReCaptcha/Response.php + // if recaptcha resulted in error, we might have to deny login + if ( isset($responseData['success']) && ($responseData['success'] == false) ) { + // PP - before we deny auth, let's make sure the error was not 'invalid secret' + // because that means the user did not configure the secret key correctly + // in this case, we prefer to let him login in and display a message to correct + // the key. Unfortunately, there is no way to check for invalid site key in code + // as it produces the same error as when you don't answer a recaptcha + if ( isset($responseData['error-codes']) && is_array($responseData['error-codes']) ) { + if ( !in_array('invalid-input-secret', $responseData['error-codes']) ) { + ZM\Error('reCaptcha authentication failed. response was: ' . print_r($responseData['error-codes'],true)); + unset($user); // unset should be ok here because we aren't in a function + return; + } else { + ZM\Error('Invalid recaptcha secret detected'); + } + } + } // end if success==false + if ( ! (empty($_REQUEST['username']) or empty($_REQUEST['password'])) ) { + $ret = validateUser($_REQUEST['username'], $_REQUEST['password']); + if ( !$ret[0] ) { + ZM\Error($ret[1]); + unset($user); // unset should be ok here because we aren't in a function + } else { + $user = $ret[0]; + } + } # end if have username and password + } // end if using reCaptcha + + // if captcha existed, it was passed + + if ( ! isset($user) ) { + $_SESSION['loginFailed'] = true; + return; + } + + $close_session = 0; + if ( !is_session_started() ) { + zm_session_start(); + $close_session = 1; + } + + $username = $_REQUEST['username']; + $password = $_REQUEST['password']; + + ZM\Info("Login successful for user \"$username\""); + $password_type = password_type($password); + + if ( $password_type == 'mysql' or $password_type == 'mysql+bcrypt' ) { + ZM\Info('Migrating password, if possible for future logins'); + migrateHash($username, $password); + } + unset($_SESSION['loginFailed']); + if ( ZM_AUTH_TYPE == 'builtin' ) { + $_SESSION['passwordHash'] = $user['Password']; + } + $_SESSION['username'] = $user['Username']; + if ( ZM_AUTH_RELAY == 'plain' ) { + // Need to save this in session, can't use the value in User because it is hashed + $_SESSION['password'] = $_REQUEST['password']; + } + zm_session_regenerate_id(); + generateAuthHash(ZM_AUTH_HASH_IPS, true); + if ( $close_session ) + session_write_close(); + + $view = 'postlogin'; +} # end if doing a login action +?> diff --git a/web/includes/actions/logout.php b/web/includes/actions/logout.php new file mode 100644 index 000000000..aecdb7683 --- /dev/null +++ b/web/includes/actions/logout.php @@ -0,0 +1,29 @@ + diff --git a/web/includes/actions/monitor.php b/web/includes/actions/monitor.php new file mode 100644 index 000000000..f1223a6f9 --- /dev/null +++ b/web/includes/actions/monitor.php @@ -0,0 +1,279 @@ + array(), + 'Controllable' => 0, + 'TrackMotion' => 0, + 'Enabled' => 0, + 'Exif' => 0, + 'RTSPDescribe' => 0, + 'RecordAudio' => 0, + 'Method' => 'raw', + 'GroupIds' => array(), + 'LinkedMonitors' => array() + ); + + # Checkboxes don't return an element in the POST data, so won't be present in newMonitor. + # So force a value for these fields + foreach ( $types as $field => $value ) { + if ( ! isset($_REQUEST['newMonitor'][$field] ) ) { + $_REQUEST['newMonitor'][$field] = $value; + } + } # end foreach type + + if ( $_REQUEST['newMonitor']['ServerId'] == 'auto' ) { + $_REQUEST['newMonitor']['ServerId'] = dbFetchOne( + 'SELECT Id FROM Servers WHERE Status=\'Running\' ORDER BY FreeMem DESC, CpuLoad ASC LIMIT 1', 'Id'); + ZM\Logger::Debug('Auto selecting server: Got ' . $_REQUEST['newMonitor']['ServerId']); + if ( ( !$_REQUEST['newMonitor'] ) and defined('ZM_SERVER_ID') ) { + $_REQUEST['newMonitor']['ServerId'] = ZM_SERVER_ID; + ZM\Logger::Debug('Auto selecting server to ' . ZM_SERVER_ID); + } + } + + $changes = $monitor->changes($_REQUEST['newMonitor'], $types); + $restart = false; + + if ( count($changes) ) { + if ( $monitor->Id() ) { + + # If we change anything that changes the shared mem size, zma can complain. So let's stop first. + if ( $monitor->Type() != 'WebSite' ) { + $monitor->zmaControl('stop'); + $monitor->zmcControl('stop'); + } + + # These are used in updating zones + $oldW = $monitor->Width(); + $oldH = $monitor->Height(); + + if ( $monitor->save($changes) ) { + + // Groups will be added below + if ( isset($changes['Name']) or isset($changes['StorageId']) ) { + // creating symlinks when symlink already exists reports errors, but is perfectly ok + error_reporting(0); + + $OldStorage = $monitor->Storage(); + $saferOldName = basename($monitor->Name()); + if ( file_exists($OldStorage->Path().'/'.$saferOldName) ) + unlink($OldStorage->Path().'/'.$saferOldName); + + $NewStorage = new ZM\Storage($_REQUEST['newMonitor']['StorageId']); + if ( !file_exists($NewStorage->Path().'/'.$mid) ) { + if ( !mkdir($NewStorage->Path().'/'.$mid, 0755) ) { + ZM\Error('Unable to mkdir ' . $NewStorage->Path().'/'.$mid); + } + } + $saferNewName = basename($_REQUEST['newMonitor']['Name']); + $link_path = $NewStorage->Path().'/'.$saferNewName; + if ( !symlink($NewStorage->Path().'/'.$mid, $link_path) ) { + if ( ! ( file_exists($link_path) and is_link($link_path) ) ) { + ZM\Warning('Unable to symlink ' . $NewStorage->Path().'/'.$mid . ' to ' . $NewStorage->Path().'/'.$saferNewName); + } + } + } // end if Name or Storage Area Change + + if ( isset($changes['Width']) || isset($changes['Height']) ) { + $newW = $_REQUEST['newMonitor']['Width']; + $newH = $_REQUEST['newMonitor']['Height']; + + $zones = dbFetchAll('SELECT * FROM Zones WHERE MonitorId=?', NULL, array($mid)); + + if ( ($newW == $oldH) and ($newH == $oldW) ) { + foreach ( $zones as $zone ) { + $newZone = $zone; + # Rotation, no change to area etc just swap the coords + $newZone = $zone; + $points = coordsToPoints($zone['Coords']); + for ( $i = 0; $i < count($points); $i++ ) { + $x = $points[$i]['x']; + $points[$i]['x'] = $points[$i]['y']; + $points[$i]['y'] = $x; + + if ( $points[$i]['x'] > ($newW-1) ) { + ZM\Warning("Correcting x {$points[$i]['x']} > $newW of zone {$newZone['Name']} as it extends outside the new dimensions"); + $points[$i]['x'] = ($newW-1); + } + if ( $points[$i]['y'] > ($newH-1) ) { + ZM\Warning("Correcting y {$points[$i]['y']} $newH of zone {$newZone['Name']} as it extends outside the new dimensions"); + $points[$i]['y'] = ($newH-1); + } + } + + $newZone['Coords'] = pointsToCoords($points); + $changes = getFormChanges($zone, $newZone, $types); + + if ( count($changes) ) { + dbQuery('UPDATE Zones SET '.implode(', ', $changes).' WHERE MonitorId=? AND Id=?', + array($mid, $zone['Id'])); + } + } # end foreach zone + } else { + $newA = $newW * $newH; + $oldA = $oldW * $oldH; + + foreach ( $zones as $zone ) { + $newZone = $zone; + $points = coordsToPoints($zone['Coords']); + for ( $i = 0; $i < count($points); $i++ ) { + $points[$i]['x'] = intval(($points[$i]['x']*($newW-1))/($oldW-1)); + $points[$i]['y'] = intval(($points[$i]['y']*($newH-1))/($oldH-1)); + if ( $points[$i]['x'] > ($newW-1) ) { + ZM\Warning("Correcting x of zone {$newZone['Name']} as it extends outside the new dimensions"); + $points[$i]['x'] = ($newW-1); + } + if ( $points[$i]['y'] > ($newH-1) ) { + ZM\Warning("Correcting y of zone {$newZone['Name']} as it extends outside the new dimensions"); + $points[$i]['y'] = ($newH-1); + } + } + $newZone['Coords'] = pointsToCoords($points); + $newZone['Area'] = intval(round(($zone['Area']*$newA)/$oldA)); + $newZone['MinAlarmPixels'] = intval(round(($newZone['MinAlarmPixels']*$newA)/$oldA)); + $newZone['MaxAlarmPixels'] = intval(round(($newZone['MaxAlarmPixels']*$newA)/$oldA)); + $newZone['MinFilterPixels'] = intval(round(($newZone['MinFilterPixels']*$newA)/$oldA)); + $newZone['MaxFilterPixels'] = intval(round(($newZone['MaxFilterPixels']*$newA)/$oldA)); + $newZone['MinBlobPixels'] = intval(round(($newZone['MinBlobPixels']*$newA)/$oldA)); + $newZone['MaxBlobPixels'] = intval(round(($newZone['MaxBlobPixels']*$newA)/$oldA)); + + $changes = getFormChanges($zone, $newZone, $types); + + if ( count($changes) ) { + dbQuery('UPDATE Zones SET '.implode(', ', $changes).' WHERE MonitorId=? AND Id=?', + array($mid, $zone['Id'])); + } + } // end foreach zone + } // end if rotation or just size change + } // end if changes in width or height + } // end if successful save + $restart = true; + } else { // new monitor + // Can only create new monitors if we are not restricted to specific monitors +# FIXME This is actually a race condition. Should lock the table. + $maxSeq = dbFetchOne('SELECT MAX(Sequence) AS MaxSequence FROM Monitors', 'MaxSequence'); + $changes['Sequence'] = $maxSeq+1; + + if ( $monitor->save($changes) ) { + $mid = $monitor->Id(); + $zoneArea = $_REQUEST['newMonitor']['Width'] * $_REQUEST['newMonitor']['Height']; + dbQuery("INSERT INTO Zones SET MonitorId = ?, Name = 'All', Type = 'Active', Units = 'Percent', NumCoords = 4, Coords = ?, Area=?, AlarmRGB = 0xff0000, CheckMethod = 'Blobs', MinPixelThreshold = 25, MinAlarmPixels=?, MaxAlarmPixels=?, FilterX = 3, FilterY = 3, MinFilterPixels=?, MaxFilterPixels=?, MinBlobPixels=?, MinBlobs = 1", array( $mid, sprintf( "%d,%d %d,%d %d,%d %d,%d", 0, 0, $_REQUEST['newMonitor']['Width']-1, 0, $_REQUEST['newMonitor']['Width']-1, $_REQUEST['newMonitor']['Height']-1, 0, $_REQUEST['newMonitor']['Height']-1 ), $zoneArea, intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*2)/100) ) ); + //$view = 'none'; + $Storage = $monitor->Storage(); + + error_reporting(0); + mkdir($Storage->Path().'/'.$mid, 0755); + $saferName = basename($_REQUEST['newMonitor']['Name']); + symlink($mid, $Storage->Path().'/'.$saferName); + + } else { + ZM\Error('Error saving new Monitor.'); + return; + } + } + + if ( isset($changes['GroupIds']) ) { + dbQuery('DELETE FROM Groups_Monitors WHERE MonitorId=?', array($mid)); + foreach ( $changes['GroupIds'] as $group_id ) { + dbQuery('INSERT INTO Groups_Monitors (GroupId, MonitorId) VALUES (?,?)', array($group_id, $mid)); + } + } // end if there has been a change of groups + + $restart = true; + } else { + ZM\Logger::Debug('No action due to no changes to Monitor'); + } # end if count(changes) + + if ( !$mid ) { + ZM\Error("We should have a mid by now. Something went wrong."); + return; + } + + if ( ZM_OPT_X10 ) { + $x10Changes = getFormChanges($x10Monitor, $_REQUEST['newX10Monitor']); + + if ( count($x10Changes) ) { + if ( $x10Monitor && isset($_REQUEST['newX10Monitor']) ) { + dbQuery('UPDATE TriggersX10 SET '.implode(', ', $x10Changes).' WHERE MonitorId=?', array($mid)); + } elseif ( !$user['MonitorIds'] ) { + if ( !$x10Monitor ) { + dbQuery('INSERT INTO TriggersX10 SET MonitorId = ?, '.implode(', ', $x10Changes), array($mid)); + } else { + dbQuery('DELETE FROM TriggersX10 WHERE MonitorId = ?', array($mid)); + } + } + $restart = true; + } # end if has x10Changes + } # end if ZM_OPT_X10 + + if ( $restart ) { + if ( $monitor->Function() != 'None' and $monitor->Type() != 'WebSite' ) { + $monitor->zmcControl('start'); + if ( ($monitor->Function() == 'Modect' or $monitor->Function() == 'Mocord') and $monitor->Enabled() ) + $monitor->zmaControl('start'); + + if ( $monitor->Controllable() ) { + require_once('includes/control_functions.php'); + $monitor->sendControlCommand('quit'); + } + } + // really should thump zmwatch and maybe zmtrigger too. + //daemonControl( 'restart', 'zmwatch.pl' ); + $refreshParent = true; + } // end if restart + $view = 'none'; +} else { + ZM\Warning("Unknown action $action in Monitor"); +} // end if action == Delete +?> diff --git a/web/includes/actions/monitors.php b/web/includes/actions/monitors.php new file mode 100644 index 000000000..8f56b0e3d --- /dev/null +++ b/web/includes/actions/monitors.php @@ -0,0 +1,52 @@ +Type() != 'WebSite' ) { + $Monitor->zmaControl('stop'); + $Monitor->zmcControl('stop'); + } + $Monitor->save($_REQUEST['newMonitor']); + if ( $Monitor->Function() != 'None' && $Monitor->Type() != 'WebSite' ) { + $Monitor->zmcControl('start'); + if ( $Monitor->Enabled() ) { + $Monitor->zmaControl('start'); + } + } + } // end foreach mid + $refreshParent = true; + $view = 'none'; +} else { + ZM\Warning("Unknown action $action in Monitor"); +} // end if action == Delete +?> diff --git a/web/includes/actions/montage.php b/web/includes/actions/montage.php new file mode 100644 index 000000000..207c9a0f0 --- /dev/null +++ b/web/includes/actions/montage.php @@ -0,0 +1,48 @@ +Name($_REQUEST['Name']); + } else { + $Layout = new ZM\MontageLayout($_REQUEST['zmMontageLayout']); + } + $Layout->Positions($_REQUEST['Positions']); + $Layout->save(); + session_start(); + $_SESSION['zmMontageLayout'] = $Layout->Id(); + setcookie('zmMontageLayout', $Layout->Id(), 1); + session_write_close(); + $redirect = '?view=montage'; + } // end if save + + } # end if isset($_REQUEST['object'] ) +} # end if isset($_REQUEST['object'] ) +?> diff --git a/web/includes/actions/options.php b/web/includes/actions/options.php new file mode 100644 index 000000000..9f1655632 --- /dev/null +++ b/web/includes/actions/options.php @@ -0,0 +1,102 @@ + diff --git a/web/includes/actions/privacy.php b/web/includes/actions/privacy.php new file mode 100644 index 000000000..5877bb60d --- /dev/null +++ b/web/includes/actions/privacy.php @@ -0,0 +1,42 @@ + diff --git a/web/includes/actions/server.php b/web/includes/actions/server.php new file mode 100644 index 000000000..1ffe4933d --- /dev/null +++ b/web/includes/actions/server.php @@ -0,0 +1,53 @@ + diff --git a/web/includes/actions/settings.php b/web/includes/actions/settings.php new file mode 100644 index 000000000..c33f3a54c --- /dev/null +++ b/web/includes/actions/settings.php @@ -0,0 +1,49 @@ + diff --git a/web/includes/actions/shutdown.php b/web/includes/actions/shutdown.php new file mode 100644 index 000000000..4d7a4ee78 --- /dev/null +++ b/web/includes/actions/shutdown.php @@ -0,0 +1,44 @@ +&1", $output, $rc); + #exec('sudo -n /bin/systemctl poweroff -i 2>&1', $output, $rc); + ZM\Logger::Debug("Shutdown output $rc " . implode("\n",$output)); + #ZM\Logger::Debug("Shutdown output " . shell_exec('/bin/systemctl poweroff -i 2>&1')); + } else if ( $action == 'restart' ) { + $output = array(); + exec('sudo -n '.ZM_PATH_SHUTDOWN." -r $when 2>&1", $output); + #exec('sudo -n /bin/systemctl reboot -i 2>&1', $output); + ZM\Logger::Debug("Shutdown output " . implode("\n",$output)); + } else if ( $action == 'cancel' ) { + $output = array(); + exec('sudo '.ZM_PATH_SHUTDOWN.' -c 2>&1', $output); + } +} # end if action +?> diff --git a/web/includes/actions/state.php b/web/includes/actions/state.php new file mode 100644 index 000000000..0f7a9e9a5 --- /dev/null +++ b/web/includes/actions/state.php @@ -0,0 +1,49 @@ + diff --git a/web/includes/actions/storage.php b/web/includes/actions/storage.php new file mode 100644 index 000000000..f60c8227d --- /dev/null +++ b/web/includes/actions/storage.php @@ -0,0 +1,49 @@ + diff --git a/web/includes/actions/user.php b/web/includes/actions/user.php new file mode 100644 index 000000000..bdbafd176 --- /dev/null +++ b/web/includes/actions/user.php @@ -0,0 +1,84 @@ + diff --git a/web/includes/actions/version.php b/web/includes/actions/version.php new file mode 100644 index 000000000..fde85427f --- /dev/null +++ b/web/includes/actions/version.php @@ -0,0 +1,52 @@ + 0 + $types = array( + 'OverloadFrames' => 'integer', + 'ExtendAlarmFrames' => 'integer', + ); + + $changes = getFormChanges($zone, $_REQUEST['newZone'], $types); + + if ( count($changes) ) { + if ( $zid > 0 ) { + dbQuery('UPDATE Zones SET '.implode(', ', $changes).' WHERE MonitorId=? AND Id=?', array($mid, $zid)); + } else { + dbQuery('INSERT INTO Zones SET MonitorId=?, '.implode(', ', $changes), array($mid)); + } + if ( daemonCheck() && ($monitor->Type() != 'WebSite') ) { + if ( $_REQUEST['newZone']['Type'] == 'Privacy' ) { + $monitor->zmaControl('stop'); + $monitor->zmcControl('restart'); + $monitor->zmaControl('start'); + } else { + $monitor->zmaControl('restart'); + } + } + if ( ($_REQUEST['newZone']['Type'] == 'Privacy') && $monitor->Controllable() ) { + $monitor->sendControlCommand('quit'); + } + $refreshParent = true; + } // end if changes + $view = 'none'; + } // end if action +} // end if $mid and canEdit($mid) +?> diff --git a/web/includes/actions/zones.php b/web/includes/actions/zones.php new file mode 100644 index 000000000..babb4fa7b --- /dev/null +++ b/web/includes/actions/zones.php @@ -0,0 +1,49 @@ +Type() != 'WebSite' ) { + zmaControl($mid, 'stop'); + if ( $restart_zmc ) + zmcControl($mid, 'restart'); + zmaControl($mid, 'start'); + } // end if daemonCheck() + $refreshParent = true; + } // end if isset($_REQUEST['markZids']) + } // end if action +} // end if $mid and canEdit($mid) + +?> diff --git a/web/includes/auth.php b/web/includes/auth.php index b14323103..f958463c3 100644 --- a/web/includes/auth.php +++ b/web/includes/auth.php @@ -2,137 +2,217 @@ // // ZoneMinder auth library, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes -// +// // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// +// +// +require_once('session.php'); +require_once(__DIR__.'/../vendor/autoload.php'); +use \Firebase\JWT\JWT; -function userLogin($username, $password='', $passwordHashed=false) { - global $user, $cookies; +function password_type($password) { + if ( $password[0] == '*' ) { + return 'mysql'; + } else if ( preg_match('/^\$2[ayb]\$.+$/', $password) ) { + return 'bcrypt'; + } else if ( substr($password, 0,4) == '-ZM-' ) { + // zmupdate.pl adds a '-ZM-' prefix to overlay encrypted passwords + // this is done so that we don't spend cycles doing two bcrypt password_verify calls + // for every wrong password entered. This will only be invoked for passwords zmupdate.pl has + // overlay hashed + return 'mysql+bcrypt'; + } + return 'plain'; +} - $sql = 'SELECT * FROM Users WHERE Enabled=1'; - $sql_values = NULL; - if ( ZM_AUTH_TYPE == 'builtin' ) { - if ( $passwordHashed ) { - $sql .= ' AND Username=? AND Password=?'; - } else { - $sql .= ' AND Username=? AND Password=password(?)'; - } - $sql_values = array($username, $password); +// this function migrates mysql hashing to bcrypt, if you are using PHP >= 5.5 +// will be called after successful login, only if mysql hashing is detected +function migrateHash($user, $pass) { + if ( function_exists('password_hash') ) { + ZM\Info("Migrating $user to bcrypt scheme"); + // let it generate its own salt, and ensure bcrypt as PASSWORD_DEFAULT may change later + // we can modify this later to support argon2 etc as switch to its own password signature detection + $bcrypt_hash = password_hash($pass, PASSWORD_BCRYPT); + //ZM\Info ("hased bcrypt $pass is $bcrypt_hash"); + $update_password_sql = 'UPDATE Users SET Password=\''.$bcrypt_hash.'\' WHERE Username=\''.$user.'\''; + dbQuery($update_password_sql); + # Since password field has changed, existing auth_hash is no longer valid + generateAuthHash(ZM_AUTH_HASH_IPS, true); } else { - $sql .= ' AND Username=?'; - $sql_values = array($username); + ZM\Info('Cannot migrate password scheme to bcrypt, as you are using PHP < 5.3'); + return; } - session_start(); - $_SESSION['username'] = $username; - if ( ZM_AUTH_RELAY == 'plain' ) { - // Need to save this in session - $_SESSION['password'] = $password; +} + +// core function used to load a User record by username and password +function validateUser($username='', $password='') { + + $sql = 'SELECT * FROM Users WHERE Enabled=1 AND Username=?'; + // local user, shouldn't affect the global user + $user = dbFetchOne($sql, NULL, array($username)); + + if ( ! $user ) { + return array(false, "Could not retrieve user $username details"); } - $_SESSION['remoteAddr'] = $_SERVER['REMOTE_ADDR']; // To help prevent session hijacking - if ( $dbUser = dbFetchOne($sql, NULL, $sql_values) ) { - Info("Login successful for user \"$username\""); - $_SESSION['user'] = $user = $dbUser; - unset($_SESSION['loginFailed']); - if ( ZM_AUTH_TYPE == 'builtin' ) { - $_SESSION['passwordHash'] = $user['Password']; - } - session_regenerate_id(); - } else { - Warning("Login denied for user \"$username\""); - $_SESSION['loginFailed'] = true; - unset($user); + + switch ( password_type($user['Password']) ) { + case 'mysql' : + // We assume we don't need to support mysql < 4.1 + // Starting MY SQL 4.1, mysql concats a '*' in front of its password hash + // https://blog.pythian.com/hashing-algorithm-in-mysql-password-2/ + ZM\Logger::Debug('Saved password is using MYSQL password function'); + $input_password_hash = '*'.strtoupper(sha1(sha1($password, true))); + $password_correct = ($user['Password'] == $input_password_hash); + break; + case 'bcrypt' : + ZM\Logger::Debug('bcrypt signature found, assumed bcrypt password'); + $password_correct = password_verify($password, $user['Password']); + break; + case 'mysql+bcrypt' : + // zmupdate.pl adds a '-ZM-' prefix to overlay encrypted passwords + // this is done so that we don't spend cycles doing two bcrypt password_verify calls + // for every wrong password entered. This will only be invoked for passwords zmupdate.pl has + // overlay hashed + ZM\Logger::Debug("Detected bcrypt overlay hashing for $username"); + $bcrypt_hash = substr($user['Password'], 4); + $mysql_encoded_password = '*'.strtoupper(sha1(sha1($password, true))); + ZM\Logger::Debug("Comparing password $mysql_encoded_password to bcrypt hash: $bcrypt_hash"); + $password_correct = password_verify($mysql_encoded_password, $bcrypt_hash); + break; + default: + // we really should nag the user not to use plain + ZM\Warning('assuming plain text password as signature is not known. Please do not use plain, it is very insecure'); + $password_correct = ($user['Password'] == $password); + } // switch password_type + + if ( $password_correct ) { + return array($user, 'OK'); } - session_write_close(); -} # end function userLogin + return array(false, "Login denied for user \"$username\""); +} # end function validateUser function userLogout() { global $user; - Info('User "'.$user['Username'].'" logged out'); - session_start(); - unset($_SESSION['user']); - unset($user); - - session_destroy(); + ZM\Info('User "'.$user['Username'].'" logged out'); + $user = null;// unset only clears the local variable + zm_session_clear(); } +function validateToken($token, $allowed_token_type='access') { + global $user; + $key = ZM_AUTH_HASH_SECRET; + //if (ZM_AUTH_HASH_IPS) $key .= $_SERVER['REMOTE_ADDR']; + try { + $decoded_token = JWT::decode($token, $key, array('HS256')); + } catch (Exception $e) { + ZM\Error("Unable to authenticate user. error decoding JWT token:".$e->getMessage()); + return array(false, $e->getMessage()); + } + + // convert from stdclass to array + $jwt_payload = json_decode(json_encode($decoded_token), true); + + $type = $jwt_payload['type']; + if ( $type != $allowed_token_type ) { + ZM\Error("Token type mismatch. Expected $allowed_token_type but got $type"); + return array(false, 'Incorrect token type'); + } + $username = $jwt_payload['user']; + $sql = 'SELECT * FROM Users WHERE Enabled=1 AND Username=?'; + $saved_user_details = dbFetchOne($sql, NULL, array($username)); + + if ( $saved_user_details ) { + $issuedAt = $jwt_payload['iat']; + $minIssuedAt = $saved_user_details['TokenMinExpiry']; + + if ( $issuedAt < $minIssuedAt ) { + ZM\Error("Token revoked for $username. Please generate a new token"); + $user = null;// unset only clears the local variable + return array(false, 'Token revoked. Please re-generate'); + } + $user = $saved_user_details; + return array($user, 'OK'); + } + ZM\Error("Could not retrieve user $username details"); + $user = null;// unset only clears the local variable + return array(false, 'No such user/credentials'); +} // end function validateToken($token, $allowed_token_type='access') + function getAuthUser($auth) { - if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == 'hashed' && !empty($auth) ) { + if ( ZM_OPT_USE_AUTH && (ZM_AUTH_RELAY == 'hashed') && !empty($auth) ) { $remoteAddr = ''; if ( ZM_AUTH_HASH_IPS ) { $remoteAddr = $_SERVER['REMOTE_ADDR']; if ( !$remoteAddr ) { - Error("Can't determine remote address for authentication, using empty string"); + ZM\Error("Can't determine remote address for authentication, using empty string"); $remoteAddr = ''; } } + $values = array(); if ( isset($_SESSION['username']) ) { # Most of the time we will be logged in already and the session will have our username, so we can significantly speed up our hash testing by only looking at our user. # Only really important if you have a lot of users. - $sql = "SELECT * FROM Users WHERE Enabled = 1 AND Username='".$_SESSION['username']."'"; + $sql = 'SELECT * FROM Users WHERE Enabled = 1 AND Username=?'; + array_push($values, $_SESSION['username']); } else { $sql = 'SELECT * FROM Users WHERE Enabled = 1'; } - foreach ( dbFetchAll($sql) as $user ) { + foreach ( dbFetchAll($sql, NULL, $values) as $user ) { $now = time(); - for ( $i = 0; $i < ZM_AUTH_HASH_TTL; $i++, $now -= (3600) ) { // Try for last two hours + for ( $i = 0; $i < ZM_AUTH_HASH_TTL; $i++, $now -= 3600 ) { // Try for last TTL hours $time = localtime($now); $authKey = ZM_AUTH_HASH_SECRET.$user['Username'].$user['Password'].$remoteAddr.$time[2].$time[3].$time[4].$time[5]; $authHash = md5($authKey); if ( $auth == $authHash ) { return $user; - } + } // end if $auth == $authHash } // end foreach hour } // end foreach user } // end if using auth hash - Error("Unable to authenticate user from auth hash '$auth'"); - return false; + ZM\Error("Unable to authenticate user from auth hash '$auth'"); + return null; } // end getAuthUser($auth) function generateAuthHash($useRemoteAddr, $force=false) { - if ( ZM_OPT_USE_AUTH and ZM_AUTH_RELAY == 'hashed' and isset($_SESSION['username']) and $_SESSION['passwordHash'] ) { - # regenerate a hash at half the liftetime of a hash, an hour is 3600 so half is 1800 + global $user; + if ( ZM_OPT_USE_AUTH and (ZM_AUTH_RELAY == 'hashed') and isset($user['Username']) and isset($user['Password']) ) { $time = time(); + + # We use 1800 so that we regenerate the hash at half the TTL $mintime = $time - ( ZM_AUTH_HASH_TTL * 1800 ); - if ( $force or ( !isset($_SESSION['AuthHash']) ) or ( $_SESSION['AuthHashGeneratedAt'] < $mintime ) ) { + # Appending the remoteAddr prevents us from using an auth hash generated for a different ip + if ( $force or ( !isset($_SESSION['AuthHash'.$_SESSION['remoteAddr']]) ) or ( $_SESSION['AuthHashGeneratedAt'] < $mintime ) ) { # Don't both regenerating Auth Hash if an hour hasn't gone by yet $local_time = localtime(); $authKey = ''; if ( $useRemoteAddr ) { - $authKey = ZM_AUTH_HASH_SECRET.$_SESSION['username'].$_SESSION['passwordHash'].$_SESSION['remoteAddr'].$local_time[2].$local_time[3].$local_time[4].$local_time[5]; + $authKey = ZM_AUTH_HASH_SECRET.$user['Username'].$user['Password'].$_SESSION['remoteAddr'].$local_time[2].$local_time[3].$local_time[4].$local_time[5]; } else { - $authKey = ZM_AUTH_HASH_SECRET.$_SESSION['username'].$_SESSION['passwordHash'].$local_time[2].$local_time[3].$local_time[4].$local_time[5]; + $authKey = ZM_AUTH_HASH_SECRET.$user['Username'].$user['Password'].$local_time[2].$local_time[3].$local_time[4].$local_time[5]; } - #Logger::Debug("Generated using hour:".$local_time[2] . ' mday:' . $local_time[3] . ' month:'.$local_time[4] . ' year: ' . $local_time[5] ); + #ZM\Logger::Debug("Generated using hour:".$local_time[2] . ' mday:' . $local_time[3] . ' month:'.$local_time[4] . ' year: ' . $local_time[5] ); $auth = md5($authKey); - if ( !$force ) { - session_start(); - $_SESSION['AuthHash'] = $auth; - $_SESSION['AuthHashGeneratedAt'] = $time; - session_write_close(); - } else { - return $auth; - } - #Logger::Debug("Generated new auth $auth at " . $_SESSION['AuthHashGeneratedAt']. " using $authKey" ); - #} else { - #Logger::Debug("Using cached auth " . $_SESSION['AuthHash'] ." beacuse generatedat:" . $_SESSION['AuthHashGeneratedAt'] . ' < now:'. $time . ' - ' . ZM_AUTH_HASH_TTL . ' * 1800 = '. $mintime); + $_SESSION['AuthHash'.$_SESSION['remoteAddr']] = $auth; + $_SESSION['AuthHashGeneratedAt'] = $time; + # Because we don't write out the session, it shouldn't actually get written out to disk. However if it does, the GeneratedAt should protect us. } # end if AuthHash is not cached - return $_SESSION['AuthHash']; + return $_SESSION['AuthHash'.$_SESSION['remoteAddr']]; } # end if using AUTH and AUTH_RELAY return ''; } @@ -140,19 +220,74 @@ function generateAuthHash($useRemoteAddr, $force=false) { function visibleMonitor($mid) { global $user; - return ( empty($user['MonitorIds']) || in_array($mid, explode(',', $user['MonitorIds'])) ); + return ( $user && empty($user['MonitorIds']) || in_array($mid, explode(',', $user['MonitorIds'])) ); } function canView($area, $mid=false) { global $user; - return ( ($user[$area] == 'View' || $user[$area] == 'Edit') && ( !$mid || visibleMonitor($mid) ) ); + return ( $user && ($user[$area] == 'View' || $user[$area] == 'Edit') && ( !$mid || visibleMonitor($mid) ) ); } function canEdit($area, $mid=false) { global $user; - return ( $user[$area] == 'Edit' && ( !$mid || visibleMonitor($mid) )); + return ( $user && ($user[$area] == 'Edit') && ( !$mid || visibleMonitor($mid) )); } +function userFromSession() { + $user = null; // Not global + if ( isset($_SESSION['username']) ) { + if ( ZM_AUTH_HASH_LOGINS and (ZM_AUTH_RELAY == 'hashed') ) { + # Extra validation, if logged in, then the auth hash will be set in the session, so we can validate it. + # This prevent session modification to switch users + if ( isset($_SESSION['AuthHash'.$_SESSION['remoteAddr']]) ) + $user = getAuthUser($_SESSION['AuthHash'.$_SESSION['remoteAddr']]); + else + ZM\Logger::Debug("No auth hash in session, there should have been"); + } else { + # Need to refresh permissions and validate that the user still exists + $sql = 'SELECT * FROM Users WHERE Enabled=1 AND Username=?'; + $user = dbFetchOne($sql, NULL, array($_SESSION['username'])); + } + } else { + ZM\Logger::Debug('No username in session'); + } + return $user; +} + +if ( ZM_OPT_USE_AUTH ) { + if ( !empty($_REQUEST['token']) ) { + $ret = validateToken($_REQUEST['token'], 'access'); + $user = $ret[0]; + } else { + // Non token based auth + + $user = userFromSession(); + + if ( ZM_AUTH_HASH_LOGINS && empty($user) && !empty($_REQUEST['auth']) ) { + $user = getAuthUser($_REQUEST['auth']); + } else if ( + ! ( + empty($_REQUEST['username']) or + empty($_REQUEST['password']) or + (defined('ZM_OPT_USE_GOOG_RECAPTCHA') && ZM_OPT_USE_GOOG_RECAPTCHA) + ) ) { + $ret = validateUser($_REQUEST['username'], $_REQUEST['password']); + if ( !$ret[0] ) { + ZM\Error($ret[1]); + unset($user); // unset should be ok here because we aren't in a function + return; + } + $user = $ret[0]; + } + + if ( !empty($user) ) { + // generate it once here, while session is open. Value will be cached in session and return when called later on + generateAuthHash(ZM_AUTH_HASH_IPS); + } + } # end if token based auth +} else { + $user = $defaultUser; +} # end if ZM_OPT_USE_AUTH ?> diff --git a/web/includes/config.php.in b/web/includes/config.php.in index c6aa93c63..57064f22d 100644 --- a/web/includes/config.php.in +++ b/web/includes/config.php.in @@ -33,9 +33,9 @@ $configFile = ZM_CONFIG; $localConfigFile = basename($configFile); if ( file_exists( $localConfigFile ) && filesize( $localConfigFile ) > 0 ) { if ( php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']) ) - print( "Warning, overriding installed $localConfigFile file with local copy\n" ); + print("Warning, overriding installed $localConfigFile file with local copy\n"); else - error_log( "Warning, overriding installed $localConfigFile file with local copy" ); + error_log("Warning, overriding installed $localConfigFile file with local copy"); $configFile = $localConfigFile; } @@ -49,19 +49,19 @@ if ( is_dir($configSubFolder) ) { if ( is_readable($configSubFolder) ) { foreach ( glob("$configSubFolder/*.conf") as $filename ) { //error_log("processing $filename"); - $configvals = array_replace($configvals, process_configfile($filename) ); + $configvals = array_replace($configvals, process_configfile($filename)); } } else { - error_log( "WARNING: ZoneMinder configuration subfolder found but is not readable. Check folder permissions on $configSubFolder." ); + error_log("WARNING: ZoneMinder configuration subfolder found but is not readable. Check folder permissions on $configSubFolder."); } } else { - error_log( "WARNING: ZoneMinder configuration subfolder found but is not a directory. Check $configSubFolder." ); + error_log("WARNING: ZoneMinder configuration subfolder found but is not a directory. Check $configSubFolder."); } # Now that our array our finalized, define each key => value # pair in the array as a constant -foreach( $configvals as $key => $value) { - define( $key, $value ); +foreach ( $configvals as $key => $value ) { + define($key, $value); } // @@ -135,10 +135,10 @@ define( 'SCALE_BASE', 100 ); // The additional scalin define( 'STRF_FMT_DATETIME_DB', '%Y-%m-%d %H:%M:%S' ); // Strftime format for database queries, don't change define( 'MYSQL_FMT_DATETIME_SHORT', '%y/%m/%d %H:%i:%S' ); // MySQL date_format shorter format for dates with time -require_once( 'database.php' ); -require_once( 'logger.php' ); +require_once('database.php'); +require_once('logger.php'); loadConfig(); -Logger::fetch()->initialise(); +ZM\Logger::fetch()->initialise(); $GLOBALS['defaultUser'] = array( 'Username' => 'admin', @@ -158,68 +158,64 @@ $GLOBALS['defaultUser'] = array( function loadConfig( $defineConsts=true ) { global $config; - global $configCats; global $dbConn; $config = array(); - $configCat = array(); - $result = $dbConn->query( 'select * from Config order by Id asc' ); + $result = $dbConn->query('SELECT Name,Value FROM Config'); if ( !$result ) echo mysql_error(); - $monitors = array(); - while( $row = dbFetchNext( $result ) ) { + while( $row = dbFetchNext($result) ) { if ( $defineConsts ) - define( $row['Name'], $row['Value'] ); + define($row['Name'], $row['Value']); $config[$row['Name']] = $row; - if ( !($configCat = &$configCats[$row['Category']]) ) { - $configCats[$row['Category']] = array(); - $configCat = &$configCats[$row['Category']]; - } - $configCat[$row['Name']] = $row; } - //print_r( $config ); - //print_r( $configCats ); -} +} # end function loadConfig // For Human-readability, use ZM_SERVER_HOST or ZM_SERVER_NAME in zm.conf, and convert it here to a ZM_SERVER_ID if ( ! defined('ZM_SERVER_ID') ) { + require_once('Server.php'); if ( defined('ZM_SERVER_NAME') and ZM_SERVER_NAME ) { - $server_id = dbFetchOne('SELECT Id FROM Servers WHERE Name=?', 'Id', array(ZM_SERVER_NAME)); - if ( ! $server_id ) { - Error('Invalid Multi-Server configration detected. ZM_SERVER_NAME set to ' . ZM_SERVER_NAME . ' in zm.conf, but no corresponding entry found in Servers table.'); + # Use Server lookup so that it caches + $Server = ZM\Server::find_one(array('Name'=>ZM_SERVER_NAME)); + if ( !$Server ) { + ZM\Error('Invalid Multi-Server configration detected. ZM_SERVER_NAME set to ' . ZM_SERVER_NAME . ' in zm.conf, but no corresponding entry found in Servers table.'); } else { - define( 'ZM_SERVER_ID', $server_id ); + define('ZM_SERVER_ID', $Server->Id()); } } else if ( defined('ZM_SERVER_HOST') and ZM_SERVER_HOST ) { - $server_id = dbFetchOne('SELECT Id FROM Servers WHERE Name=?', 'Id', array(ZM_SERVER_HOST)); - if ( ! $server_id ) { - Error('Invalid Multi-Server configration detected. ZM_SERVER_HOST set to ' . ZM_SERVER_HOST . ' in zm.conf, but no corresponding entry found in Servers table.'); + $Server = ZM\Server::find_one(array('Name'=>ZM_SERVER_HOST)); + if ( ! $Server ) { + ZM\Error('Invalid Multi-Server configration detected. ZM_SERVER_HOST set to ' . ZM_SERVER_HOST . ' in zm.conf, but no corresponding entry found in Servers table.'); } else { - define( 'ZM_SERVER_ID', $server_id ); + define('ZM_SERVER_ID', $Server->Id()); } } } +if ( ZM_TIMEZONE ) + ini_set('date.timezone', ZM_TIMEZONE); + function process_configfile($configFile) { if ( is_readable( $configFile ) ) { $configvals = array(); - $cfg = fopen( $configFile, 'r') or Error("Could not open config file: $configFile."); + $cfg = fopen($configFile, 'r') or ZM\Error("Could not open config file: $configFile."); while ( !feof($cfg) ) { - $str = fgets( $cfg, 256 ); - if ( preg_match( '/^\s*$/', $str )) + $str = fgets($cfg, 256); + if ( preg_match('/^\s*(#.*)?$/', $str) ) { continue; - elseif ( preg_match( '/^\s*#/', $str )) - continue; - elseif ( preg_match( '/^\s*([^=\s]+)\s*=\s*(.*?)\s*$/', $str, $matches )) + } else if ( preg_match( '/^\s*([^=\s]+)\s*=\s*[\'"]*(.*?)[\'"]*\s*$/', $str, $matches )) { $configvals[$matches[1]] = $matches[2]; + } else { + ZM\Error("Malformed line in config $configFile\n$str"); + } } - fclose( $cfg ); - return( $configvals ); + fclose($cfg); + return $configvals; } else { - error_log( "WARNING: ZoneMinder configuration file found but is not readable. Check file permissions on $configFile." ); - return( false ); + error_log("WARNING: ZoneMinder configuration file found but is not readable. Check file permissions on $configFile."); + return false; } } diff --git a/web/includes/control_functions.php b/web/includes/control_functions.php index bea235668..3fe131fab 100644 --- a/web/includes/control_functions.php +++ b/web/includes/control_functions.php @@ -1,13 +1,14 @@ Control(); if ( isset($_REQUEST['xge']) || isset($_REQUEST['yge']) ) { $slow = 0.9; // Threshold for slow speed/timeouts $turbo = 0.9; // Threshold for turbo speed - if ( preg_match( '/^([a-z]+)([A-Z][a-z]+)([A-Za-z]+)+$/', $_REQUEST['control'], $matches ) ) { + if ( preg_match('/^([a-z]+)([A-Z][a-z]+)([A-Za-z]+)+$/', $_REQUEST['control'], $matches) ) { $command = $matches[1]; $mode = $matches[2]; $dirn = $matches[3]; @@ -16,22 +17,22 @@ function buildControlCommand( $monitor ) { case 'focus' : { $factor = $_REQUEST['yge']/100; - if ( $monitor->HasFocusSpeed() ) { - $speed = intval(round($monitor->MinFocusSpeed()+(($monitor->MaxFocusSpeed()-$monitor->MinFocusSpeed())*$factor))); + if ( $control->HasFocusSpeed() ) { + $speed = intval(round($control->MinFocusSpeed()+(($control->MaxFocusSpeed()-$control->MinFocusSpeed())*$factor))); $ctrlCommand .= ' --speed='.$speed; } switch( $mode ) { case 'Abs' : case 'Rel' : { - $step = intval(round($monitor->MinFocusStep()+(($monitor->MaxFocusStep()-$monitor->MinFocusStep())*$factor))); + $step = intval(round($control->MinFocusStep()+(($control->MaxFocusStep()-$control->MinFocusStep())*$factor))); $ctrlCommand .= ' --step='.$step; break; } case 'Con' : { if ( $monitor->AutoStopTimeout() ) { - $slowSpeed = intval(round($monitor->MinFocusSpeed()+(($monitor->MaxFocusSpeed()-$monitor->MinFocusSpeed())*$slow))); + $slowSpeed = intval(round($control->MinFocusSpeed()+(($control->MaxFocusSpeed()-$control->MinFocusSpeed())*$slow))); if ( $speed < $slowSpeed ) { $ctrlCommand .= ' --autostop'; } @@ -43,19 +44,19 @@ function buildControlCommand( $monitor ) { } case 'zoom' : $factor = $_REQUEST['yge']/100; - if ( $monitor->HasZoomSpeed() ) { - $speed = intval(round($monitor->MinZoomSpeed()+(($monitor->MaxZoomSpeed()-$monitor->MinZoomSpeed())*$factor))); + if ( $control->HasZoomSpeed() ) { + $speed = intval(round($control->MinZoomSpeed()+(($control->MaxZoomSpeed()-$control->MinZoomSpeed())*$factor))); $ctrlCommand .= ' --speed='.$speed; } switch( $mode ) { case 'Abs' : case 'Rel' : - $step = intval(round($monitor->MinZoomStep()+(($monitor->MaxZoomStep()-$monitor->MinZoomStep())*$factor))); + $step = intval(round($control->MinZoomStep()+(($control->MaxZoomStep()-$control->MinZoomStep())*$factor))); $ctrlCommand .= ' --step='.$step; break; case 'Con' : if ( $monitor->AutoStopTimeout() ) { - $slowSpeed = intval(round($monitor->MinZoomSpeed()+(($monitor->MaxZoomSpeed()-$monitor->MinZoomSpeed())*$slow))); + $slowSpeed = intval(round($control->MinZoomSpeed()+(($control->MaxZoomSpeed()-$control->MinZoomSpeed())*$slow))); if ( $speed < $slowSpeed ) { $ctrlCommand .= ' --autostop'; } @@ -65,42 +66,42 @@ function buildControlCommand( $monitor ) { break; case 'iris' : $factor = $_REQUEST['yge']/100; - if ( $monitor->HasIrisSpeed() ) { - $speed = intval(round($monitor->MinIrisSpeed()+(($monitor->MaxIrisSpeed()-$monitor->MinIrisSpeed())*$factor))); + if ( $control->HasIrisSpeed() ) { + $speed = intval(round($control->MinIrisSpeed()+(($control->MaxIrisSpeed()-$control->MinIrisSpeed())*$factor))); $ctrlCommand .= ' --speed='.$speed; } switch( $mode ) { case 'Abs' : case 'Rel' : - $step = intval(round($monitor->MinIrisStep()+(($monitor->MaxIrisStep()-$monitor->MinIrisStep())*$factor))); + $step = intval(round($control->MinIrisStep()+(($control->MaxIrisStep()-$control->MinIrisStep())*$factor))); $ctrlCommand .= ' --step='.$step; break; } break; case 'white' : $factor = $_REQUEST['yge']/100; - if ( $monitor->HasWhiteSpeed() ) { - $speed = intval(round($monitor->MinWhiteSpeed()+(($monitor->MaxWhiteSpeed()-$monitor->MinWhiteSpeed())*$factor))); + if ( $control->HasWhiteSpeed() ) { + $speed = intval(round($control->MinWhiteSpeed()+(($control->MaxWhiteSpeed()-$control->MinWhiteSpeed())*$factor))); $ctrlCommand .= ' --speed='.$speed; } switch( $mode ) { case 'Abs' : case 'Rel' : - $step = intval(round($monitor->MinWhiteStep()+(($monitor->MaxWhiteStep()-$monitor->MinWhiteStep())*$factor))); + $step = intval(round($control->MinWhiteStep()+(($control->MaxWhiteStep()-$control->MinWhiteStep())*$factor))); $ctrlCommand .= ' --step='.$step; break; } break; case 'gain' : $factor = $_REQUEST['yge']/100; - if ( $monitor->HasGainSpeed() ) { - $speed = intval(round($monitor->MinGainSpeed()+(($monitor->MaxGainSpeed()-$monitor->MinGainSpeed())*$factor))); + if ( $control->HasGainSpeed() ) { + $speed = intval(round($control->MinGainSpeed()+(($control->MaxGainSpeed()-$control->MinGainSpeed())*$factor))); $ctrlCommand .= ' --speed='.$speed; } switch( $mode ) { case 'Abs' : case 'Rel' : - $step = intval(round($monitor->MinGainStep()+(($monitor->MaxGainStep()-$monitor->MinGainStep())*$factor))); + $step = intval(round($control->MinGainStep()+(($control->MaxGainStep()-$control->MinGainStep())*$factor))); $ctrlCommand .= ' --step='.$step; break; } @@ -109,9 +110,9 @@ function buildControlCommand( $monitor ) { $xFactor = empty($_REQUEST['xge'])?0:$_REQUEST['xge']/100; $yFactor = empty($_REQUEST['yge'])?0:$_REQUEST['yge']/100; - if ( $monitor->Orientation() != '0' ) { + if ( $monitor->Orientation() != 'ROTATE_0' ) { $conversions = array( - '90' => array( + 'ROTATE_90' => array( 'Up' => 'Left', 'Down' => 'Right', 'Left' => 'Down', @@ -121,7 +122,7 @@ function buildControlCommand( $monitor ) { 'DownLeft' => 'DownRight', 'DownRight' => 'UpRight', ), - '180' => array( + 'ROTATE_180' => array( 'Up' => 'Down', 'Down' => 'Up', 'Left' => 'Right', @@ -131,7 +132,7 @@ function buildControlCommand( $monitor ) { 'DownLeft' => 'UpRight', 'DownRight' => 'UpLeft', ), - '270' => array( + 'ROTATE_270' => array( 'Up' => 'Right', 'Down' => 'Left', 'Left' => 'Up', @@ -141,7 +142,7 @@ function buildControlCommand( $monitor ) { 'DownLeft' => 'UpLeft', 'DownRight' => 'DownLeft', ), - 'hori' => array( + 'FLIP_HORI' => array( 'Up' => 'Up', 'Down' => 'Down', 'Left' => 'Right', @@ -151,7 +152,7 @@ function buildControlCommand( $monitor ) { 'DownLeft' => 'DownRight', 'DownRight' => 'DownLeft', ), - 'vert' => array( + 'FLIP_VERT' => array( 'Up' => 'Down', 'Down' => 'Up', 'Left' => 'Left', @@ -167,29 +168,29 @@ function buildControlCommand( $monitor ) { $dirn = $new_dirn; } - if ( $monitor->HasPanSpeed() && $xFactor ) { - if ( $monitor->HasTurboPan() ) { + if ( $control->HasPanSpeed() && $xFactor ) { + if ( $control->HasTurboPan() ) { if ( $xFactor >= $turbo ) { - $panSpeed = $monitor->TurboPanSpeed(); + $panSpeed = $control->TurboPanSpeed(); } else { $xFactor = $xFactor/$turbo; - $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor))); + $panSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$xFactor))); } } else { - $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor))); + $panSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$xFactor))); } $ctrlCommand .= ' --panspeed='.$panSpeed; } - if ( $monitor->HasTiltSpeed() && $yFactor ) { - if ( $monitor->HasTurboTilt() ) { + if ( $control->HasTiltSpeed() && $yFactor ) { + if ( $control->HasTurboTilt() ) { if ( $yFactor >= $turbo ) { - $tiltSpeed = $monitor->TurboTiltSpeed(); + $tiltSpeed = $control->TurboTiltSpeed(); } else { $yFactor = $yFactor/$turbo; - $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor))); + $tiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$yFactor))); } } else { - $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor))); + $tiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$yFactor))); } $ctrlCommand .= ' --tiltspeed='.$tiltSpeed; } @@ -197,18 +198,18 @@ function buildControlCommand( $monitor ) { case 'Rel' : case 'Abs' : if ( preg_match( '/(Left|Right)$/', $dirn ) ) { - $panStep = intval(round($monitor->MinPanStep()+(($monitor->MaxPanStep()-$monitor->MinPanStep())*$xFactor))); + $panStep = intval(round($control->MinPanStep()+(($control->MaxPanStep()-$control->MinPanStep())*$xFactor))); $ctrlCommand .= ' --panstep='.$panStep; } if ( preg_match( '/^(Up|Down)/', $dirn ) ) { - $tiltStep = intval(round($monitor->MinTiltStep()+(($monitor->MaxTiltStep()-$monitor->MinTiltStep())*$yFactor))); + $tiltStep = intval(round($control->MinTiltStep()+(($control->MaxTiltStep()-$control->MinTiltStep())*$yFactor))); $ctrlCommand .= ' --tiltstep='.$tiltStep; } break; case 'Con' : if ( $monitor->AutoStopTimeout() ) { - $slowPanSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$slow))); - $slowTiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$slow))); + $slowPanSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$slow))); + $slowTiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$slow))); if ( (!isset($panSpeed) || ($panSpeed < $slowPanSpeed)) && (!isset($tiltSpeed) || ($tiltSpeed < $slowTiltSpeed)) ) { $ctrlCommand .= ' --autostop'; } @@ -224,38 +225,38 @@ function buildControlCommand( $monitor ) { $x = deScale( $_REQUEST['x'], $_REQUEST['scale'] ); $y = deScale( $_REQUEST['y'], $_REQUEST['scale'] ); switch ( $monitor->Orientation() ) { - case '0' : - case '180' : - case 'hori' : - case 'vert' : + case 'ROTATE_0' : + case 'ROTATE_180' : + case 'FLIP_HORI' : + case 'FLIP_VERT' : $width = $monitor->Width(); $height = $monitor->Height(); break; - case '90' : - case '270' : + case 'ROTATE_90' : + case 'ROTATE_270' : $width = $monitor->Height(); $height = $monitor->Width(); break; } switch ( $monitor->Orientation() ) { - case '90' : + case 'ROTATE_90' : $tempY = $y; $y = $height - $x; $x = $tempY; break; - case '180' : + case 'ROTATE_180' : $x = $width - $x; $y = $height - $y; break; - case '270' : + case 'ROTATE_270' : $tempX = $x; $x = $width - $y; $y = $tempX; break; - case 'hori' : + case 'FLIP_HORI' : $x = $width - $x; break; - case 'vert' : + case 'FLIP_VERT' : $y = $height - $y; break; } @@ -271,24 +272,24 @@ function buildControlCommand( $monitor ) { $yFactor = ($y - $halfHeight)/$halfHeight; switch ( $monitor->Orientation() ) { - case '90' : + case 'ROTATE_90' : $tempYFactor = $y; $yFactor = -$xFactor; $xFactor = $tempYFactor; break; - case '180' : + case 'ROTATE_180' : $xFactor = -$xFactor; $yFactor = -$yFactor; break; - case '270' : + case 'ROTATE_270' : $tempXFactor = $x; $xFactor = -$yFactor; $yFactor = $tempXFactor; break; - case 'hori' : + case 'FLIP_HORI' : $xFactor = -$xFactor; break; - case 'vert' : + case 'FLIP_VERT' : $yFactor = -$yFactor; break; } @@ -319,36 +320,36 @@ function buildControlCommand( $monitor ) { $xFactor = abs($xFactor); $yFactor = abs($yFactor); - if ( $monitor->HasPanSpeed() && $xFactor ) { - if ( $monitor->HasTurboPan() ) { + if ( $control->HasPanSpeed() && $xFactor ) { + if ( $control->HasTurboPan() ) { if ( $xFactor >= $turbo ) { - $panSpeed = $monitor->TurboPanSpeed(); + $panSpeed = $control->TurboPanSpeed(); } else { $xFactor = $xFactor/$turbo; - $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor))); + $panSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$xFactor))); } } else { - $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor))); + $panSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$xFactor))); } } - if ( $monitor->HasTiltSpeed() && $yFactor ) { - if ( $monitor->HasTurboTilt() ) { + if ( $control->HasTiltSpeed() && $yFactor ) { + if ( $control->HasTurboTilt() ) { if ( $yFactor >= $turbo ) { - $tiltSpeed = $monitor->TurboTiltSpeed(); + $tiltSpeed = $control->TurboTiltSpeed(); } else { $yFactor = $yFactor/$turbo; - $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor))); + $tiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$yFactor))); } } else { - $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor))); + $tiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$yFactor))); } } if ( preg_match( '/(Left|Right)$/', $dirn ) ) { - $panStep = intval(round($monitor->MinPanStep()+(($monitor->MaxPanStep()-$monitor->MinPanStep())*$xFactor))); + $panStep = intval(round($control->MinPanStep()+(($control->MaxPanStep()-$control->MinPanStep())*$xFactor))); $ctrlCommand .= ' --panstep='.$panStep.' --panspeed='.$panSpeed; } if ( preg_match( '/^(Up|Down)/', $dirn ) ) { - $tiltStep = intval(round($monitor->MinTiltStep()+(($monitor->MaxTiltStep()-$monitor->MinTiltStep())*$yFactor))); + $tiltStep = intval(round($control->MinTiltStep()+(($control->MaxTiltStep()-$control->MinTiltStep())*$yFactor))); $ctrlCommand .= ' --tiltstep='.$tiltStep.' --tiltspeed='.$tiltSpeed; } } @@ -362,24 +363,24 @@ function buildControlCommand( $monitor ) { $yFactor = ($y - $halfHeight)/$halfHeight; switch ( $monitor->Orientation() ) { - case '90' : + case 'ROTATE_90' : $tempYFactor = $y; $yFactor = -$xFactor; $xFactor = $tempYFactor; break; - case '180' : + case 'ROTATE_180' : $xFactor = -$xFactor; $yFactor = -$yFactor; break; - case '270' : + case 'ROTATE_270' : $tempXFactor = $x; $xFactor = -$yFactor; $yFactor = $tempXFactor; break; - case 'hori' : + case 'FLIP_HORI' : $xFactor = -$xFactor; break; - case 'vert' : + case 'FLIP_VERT' : $yFactor = -$yFactor; break; } @@ -410,28 +411,28 @@ function buildControlCommand( $monitor ) { $xFactor = abs($xFactor); $yFactor = abs($yFactor); - if ( $monitor->HasPanSpeed() && $xFactor ) { - if ( $monitor->HasTurboPan() ) { + if ( $control->HasPanSpeed() && $xFactor ) { + if ( $control->HasTurboPan() ) { if ( $xFactor >= $turbo ) { - $panSpeed = $monitor->TurboPanSpeed(); + $panSpeed = $control->TurboPanSpeed(); } else { $xFactor = $xFactor/$turbo; - $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor))); + $panSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$xFactor))); } } else { - $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor))); + $panSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$xFactor))); } } - if ( $monitor->HasTiltSpeed() && $yFactor ) { - if ( $monitor->HasTurboTilt() ) { + if ( $control->HasTiltSpeed() && $yFactor ) { + if ( $control->HasTurboTilt() ) { if ( $yFactor >= $turbo ) { - $tiltSpeed = $monitor->TurboTiltSpeed(); + $tiltSpeed = $control->TurboTiltSpeed(); } else { $yFactor = $yFactor/$turbo; - $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor))); + $tiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$yFactor))); } } else { - $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor))); + $tiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$yFactor))); } } if ( preg_match( '/(Left|Right)$/', $dirn ) ) { @@ -441,8 +442,8 @@ function buildControlCommand( $monitor ) { $ctrlCommand .= ' --tiltspeed='.$tiltSpeed; } if ( $monitor->AutoStopTimeout() ) { - $slowPanSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$slow))); - $slowTiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$slow))); + $slowPanSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$slow))); + $slowTiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$slow))); if ( (!isset($panSpeed) || ($panSpeed < $slowPanSpeed)) && (!isset($tiltSpeed) || ($tiltSpeed < $slowTiltSpeed)) ) { $ctrlCommand .= ' --autostop'; } @@ -470,19 +471,19 @@ function buildControlCommand( $monitor ) { $factor = ($y+1)/$long_y; break; } - if ( $monitor->HasFocusSpeed() ) { - $speed = intval(round($monitor->MinFocusSpeed()+(($monitor->MaxFocusSpeed()-$monitor->MinFocusSpeed())*$factor))); + if ( $control->HasFocusSpeed() ) { + $speed = intval(round($control->MinFocusSpeed()+(($control->MaxFocusSpeed()-$control->MinFocusSpeed())*$factor))); $ctrlCommand .= ' --speed='.$speed; } switch( $mode ) { case 'Abs' : case 'Rel' : - $step = intval(round($monitor->MinFocusStep()+(($monitor->MaxFocusStep()-$monitor->MinFocusStep())*$factor))); + $step = intval(round($control->MinFocusStep()+(($control->MaxFocusStep()-$control->MinFocusStep())*$factor))); $ctrlCommand .= ' --step='.$step; break; case 'Con' : if ( $monitor->AutoStopTimeout() ) { - $slowSpeed = intval(round($monitor->MinFocusSpeed()+(($monitor->MaxFocusSpeed()-$monitor->MinFocusSpeed())*$slow))); + $slowSpeed = intval(round($control->MinFocusSpeed()+(($control->MaxFocusSpeed()-$control->MinFocusSpeed())*$slow))); if ( $speed < $slowSpeed ) { $ctrlCommand .= ' --autostop'; } @@ -499,19 +500,19 @@ function buildControlCommand( $monitor ) { $factor = ($y+1)/$long_y; break; } - if ( $monitor->HasZoomSpeed() ) { - $speed = intval(round($monitor->MinZoomSpeed()+(($monitor->MaxZoomSpeed()-$monitor->MinZoomSpeed())*$factor))); + if ( $control->HasZoomSpeed() ) { + $speed = intval(round($control->MinZoomSpeed()+(($control->MaxZoomSpeed()-$control->MinZoomSpeed())*$factor))); $ctrlCommand .= ' --speed='.$speed; } switch( $mode ) { case 'Abs' : case 'Rel' : - $step = intval(round($monitor->MinZoomStep()+(($monitor->MaxZoomStep()-$monitor->MinZoomStep())*$factor))); + $step = intval(round($control->MinZoomStep()+(($control->MaxZoomStep()-$control->MinZoomStep())*$factor))); $ctrlCommand .= ' --step='.$step; break; case 'Con' : if ( $monitor->AutoStopTimeout() ) { - $slowSpeed = intval(round($monitor->MinZoomSpeed()+(($monitor->MaxZoomSpeed()-$monitor->MinZoomSpeed())*$slow))); + $slowSpeed = intval(round($control->MinZoomSpeed()+(($control->MaxZoomSpeed()-$control->MinZoomSpeed())*$slow))); if ( $speed < $slowSpeed ) { $ctrlCommand .= ' --autostop'; } @@ -528,14 +529,14 @@ function buildControlCommand( $monitor ) { $factor = ($y+1)/$long_y; break; } - if ( $monitor->HasIrisSpeed() ) { - $speed = intval(round($monitor->MinIrisSpeed()+(($monitor->MaxIrisSpeed()-$monitor->MinIrisSpeed())*$factor))); + if ( $control->HasIrisSpeed() ) { + $speed = intval(round($control->MinIrisSpeed()+(($control->MaxIrisSpeed()-$control->MinIrisSpeed())*$factor))); $ctrlCommand .= ' --speed='.$speed; } switch( $mode ) { case 'Abs' : case 'Rel' : - $step = intval(round($monitor->MinIrisStep()+(($monitor->MaxIrisStep()-$monitor->MinIrisStep())*$factor))); + $step = intval(round($control->MinIrisStep()+(($control->MaxIrisStep()-$control->MinIrisStep())*$factor))); $ctrlCommand .= ' --step='.$step; break; } @@ -549,14 +550,14 @@ function buildControlCommand( $monitor ) { $factor = ($y+1)/$long_y; break; } - if ( $monitor->HasWhiteSpeed() ) { - $speed = intval(round($monitor->MinWhiteSpeed()+(($monitor->MaxWhiteSpeed()-$monitor->MinWhiteSpeed())*$factor))); + if ( $control->HasWhiteSpeed() ) { + $speed = intval(round($control->MinWhiteSpeed()+(($control->MaxWhiteSpeed()-$control->MinWhiteSpeed())*$factor))); $ctrlCommand .= ' --speed='.$speed; } switch( $mode ) { case 'Abs' : case 'Rel' : - $step = intval(round($monitor->MinWhiteStep()+(($monitor->MaxWhiteStep()-$monitor->MinWhiteStep())*$factor))); + $step = intval(round($control->MinWhiteStep()+(($control->MaxWhiteStep()-$control->MinWhiteStep())*$factor))); $ctrlCommand .= ' --step='.$step; break; } @@ -570,14 +571,14 @@ function buildControlCommand( $monitor ) { $factor = ($y+1)/$long_y; break; } - if ( $monitor->HasGainSpeed() ) { - $speed = intval(round($monitor->MinGainSpeed()+(($monitor->MaxGainSpeed()-$monitor->MinGainSpeed())*$factor))); + if ( $control->HasGainSpeed() ) { + $speed = intval(round($control->MinGainSpeed()+(($control->MaxGainSpeed()-$control->MinGainSpeed())*$factor))); $ctrlCommand .= ' --speed='.$speed; } switch( $mode ) { case 'Abs' : case 'Rel' : - $step = intval(round($monitor->MinGainStep()+(($monitor->MaxGainStep()-$monitor->MinGainStep())*$factor))); + $step = intval(round($control->MinGainStep()+(($control->MaxGainStep()-$control->MinGainStep())*$factor))); $ctrlCommand .= ' --step='.$step; break; } @@ -597,9 +598,9 @@ function buildControlCommand( $monitor ) { $xFactor = ($x+1)/$short_x; } - if ( $monitor->Orientation() != '0' ) { + if ( $monitor->Orientation() != 'ROTATE_0' ) { $conversions = array( - '90' => array( + 'ROTATE_90' => array( 'Up' => 'Left', 'Down' => 'Right', 'Left' => 'Down', @@ -609,7 +610,7 @@ function buildControlCommand( $monitor ) { 'DownLeft' => 'DownRight', 'DownRight' => 'UpRight', ), - '180' => array( + 'ROTATE_180' => array( 'Up' => 'Down', 'Down' => 'Up', 'Left' => 'Right', @@ -619,7 +620,7 @@ function buildControlCommand( $monitor ) { 'DownLeft' => 'UpRight', 'DownRight' => 'UpLeft', ), - '270' => array( + 'ROTATE_270' => array( 'Up' => 'Right', 'Down' => 'Left', 'Left' => 'Up', @@ -629,7 +630,7 @@ function buildControlCommand( $monitor ) { 'DownLeft' => 'UpLeft', 'DownRight' => 'DownLeft', ), - 'hori' => array( + 'FLIP_HORI' => array( 'Up' => 'Up', 'Down' => 'Down', 'Left' => 'Right', @@ -639,7 +640,7 @@ function buildControlCommand( $monitor ) { 'DownLeft' => 'DownRight', 'DownRight' => 'DownLeft', ), - 'vert' => array( + 'FLIP_VERT' => array( 'Up' => 'Down', 'Down' => 'Up', 'Left' => 'Left', @@ -655,29 +656,29 @@ function buildControlCommand( $monitor ) { $dirn = $new_dirn; } - if ( $monitor->HasPanSpeed() && $xFactor ) { - if ( $monitor->HasTurboPan() ) { + if ( $control->HasPanSpeed() && $xFactor ) { + if ( $control->HasTurboPan() ) { if ( $xFactor >= $turbo ) { - $panSpeed = $monitor->TurboPanSpeed(); + $panSpeed = $control->TurboPanSpeed(); } else { $xFactor = $xFactor/$turbo; - $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor))); + $panSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$xFactor))); } } else { - $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor))); + $panSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$xFactor))); } $ctrlCommand .= ' --panspeed='.$panSpeed; } - if ( $monitor->HasTiltSpeed() && $yFactor ) { - if ( $monitor->HasTurboTilt() ) { + if ( $control->HasTiltSpeed() && $yFactor ) { + if ( $control->HasTurboTilt() ) { if ( $yFactor >= $turbo ) { - $tiltSpeed = $monitor->TurboTiltSpeed(); + $tiltSpeed = $control->TurboTiltSpeed(); } else { $yFactor = $yFactor/$turbo; - $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor))); + $tiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$yFactor))); } } else { - $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor))); + $tiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$yFactor))); } $ctrlCommand .= ' --tiltspeed='.$tiltSpeed; } @@ -685,18 +686,18 @@ function buildControlCommand( $monitor ) { case 'Rel' : case 'Abs' : if ( preg_match( '/(Left|Right)$/', $dirn ) ) { - $panStep = intval(round($monitor->MinPanStep()+(($monitor->MaxPanStep()-$monitor->MinPanStep())*$xFactor))); + $panStep = intval(round($control->MinPanStep()+(($control->MaxPanStep()-$control->MinPanStep())*$xFactor))); $ctrlCommand .= ' --panstep='.$panStep; } if ( preg_match( '/^(Up|Down)/', $dirn ) ) { - $tiltStep = intval(round($monitor->MinTiltStep()+(($monitor->MaxTiltStep()-$monitor->MinTiltStep())*$yFactor))); + $tiltStep = intval(round($control->MinTiltStep()+(($control->MaxTiltStep()-$control->MinTiltStep())*$yFactor))); $ctrlCommand .= ' --tiltstep='.$tiltStep; } break; case 'Con' : if ( $monitor->AutoStopTimeout() ) { - $slowPanSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$slow))); - $slowTiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$slow))); + $slowPanSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$slow))); + $slowTiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$slow))); if ( (!isset($panSpeed) || ($panSpeed < $slowPanSpeed)) && (!isset($tiltSpeed) || ($tiltSpeed < $slowTiltSpeed)) ) { $ctrlCommand .= ' --autostop'; } @@ -716,12 +717,16 @@ function buildControlCommand( $monitor ) { if ( canEdit( 'Control' ) ) { $preset = validInt($_REQUEST['preset']); $newLabel = validJsStr($_REQUEST['newLabel']); - $row = dbFetchOne( 'SELECT * FROM ControlPresets WHERE MonitorId = ? AND Preset = ?', NULL, array( $monitor->Id(), $preset ) ); + $row = dbFetchOne( + 'SELECT * FROM `ControlPresets` WHERE `MonitorId` = ? AND `Preset`=?', + NULL, array($monitor->Id(), $preset)); if ( $newLabel != $row['Label'] ) { if ( $newLabel ) { - dbQuery( 'REPLACE INTO ControlPresets ( MonitorId, Preset, Label ) VALUES ( ?, ?, ? )', array( $monitor->Id(), $preset, $newLabel ) ); + dbQuery('REPLACE INTO `ControlPresets` (`MonitorId`, `Preset`, `Label`) VALUES ( ?, ?, ? )', + array($monitor->Id(), $preset, $newLabel)); } else { - dbQuery( 'DELETE FROM ControlPresets WHERE MonitorId = ? AND Preset = ?', array( $monitor->Id(), $preset ) ); + dbQuery('DELETE FROM `ControlPresets` WHERE `MonitorId`=? AND `Preset`=?', + array($monitor->Id(), $preset)); } } $ctrlCommand .= ' --preset='.$preset; @@ -732,32 +737,6 @@ function buildControlCommand( $monitor ) { } } $ctrlCommand .= ' --command='.$_REQUEST['control']; - return( $ctrlCommand ); + return $ctrlCommand; } -function sendControlCommand($mid,$command) { - // Either connects to running zmcontrol.pl or runs zmcontrol.pl to send the command. - $socket = socket_create( AF_UNIX, SOCK_STREAM, 0 ); - if ( $socket < 0 ) { - Fatal( 'socket_create() failed: '.socket_strerror($socket) ); - } - $sockFile = ZM_PATH_SOCKS.'/zmcontrol-'.$mid.'.sock'; - if ( @socket_connect( $socket, $sockFile ) ) { - $options = array(); - foreach ( explode( ' ', $command ) as $option ) { - if ( preg_match( '/--([^=]+)(?:=(.+))?/', $option, $matches ) ) { - $options[$matches[1]] = $matches[2]?$matches[2]:1; - } - } - $optionString = jsonEncode( $options ); - if ( !socket_write( $socket, $optionString ) ) { - Fatal( "Can't write to control socket: ".socket_strerror(socket_last_error($socket)) ); - } - socket_close( $socket ); - } else if ( $command != 'quit' ) { - $command .= ' --id='.$mid; - - // Can't connect so use script - $ctrlOutput = exec( escapeshellcmd( $command ) ); - } -} // end function sendControlCommand( $mid, $command ) diff --git a/web/includes/csrf/csrf-magic.js b/web/includes/csrf/csrf-magic.js index 0989c1065..06ed62076 100644 --- a/web/includes/csrf/csrf-magic.js +++ b/web/includes/csrf/csrf-magic.js @@ -9,183 +9,185 @@ // The wrapper must be set BEFORE onreadystatechange is written to, since // a bug in ActiveXObject prevents us from properly testing for it. CsrfMagic = function(real) { - // try to make it ourselves, if you didn't pass it - if (!real) try { real = new XMLHttpRequest; } catch (e) {;} - if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) {;} - if (!real) try { real = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {;} - if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP.4.0'); } catch (e) {;} - this.csrf = real; - // properties - var csrfMagic = this; - real.onreadystatechange = function() { - csrfMagic._updateProps(); - return csrfMagic.onreadystatechange ? csrfMagic.onreadystatechange() : null; - }; + // try to make it ourselves, if you didn't pass it + if (!real) try {real = new XMLHttpRequest;} catch (e) {;} + if (!real) try {real = new ActiveXObject('Msxml2.XMLHTTP');} catch (e) {;} + if (!real) try {real = new ActiveXObject('Microsoft.XMLHTTP');} catch (e) {;} + if (!real) try {real = new ActiveXObject('Msxml2.XMLHTTP.4.0');} catch (e) {;} + this.csrf = real; + // properties + var csrfMagic = this; + real.onreadystatechange = function() { csrfMagic._updateProps(); -} + return csrfMagic.onreadystatechange ? csrfMagic.onreadystatechange() : null; + }; + csrfMagic._updateProps(); +}; CsrfMagic.prototype = { - open: function(method, url, async, username, password) { - if (method == 'POST') this.csrf_isPost = true; - // deal with Opera bug, thanks jQuery - if (username) return this.csrf_open(method, url, async, username, password); - else return this.csrf_open(method, url, async); - }, - csrf_open: function(method, url, async, username, password) { - if (username) return this.csrf.open(method, url, async, username, password); - else return this.csrf.open(method, url, async); - }, + open: function(method, url, async, username, password) { + if (method == 'POST') this.csrf_isPost = true; + // deal with Opera bug, thanks jQuery + if (username) return this.csrf_open(method, url, async, username, password); + else return this.csrf_open(method, url, async); + }, + csrf_open: function(method, url, async, username, password) { + if (username) return this.csrf.open(method, url, async, username, password); + else return this.csrf.open(method, url, async); + }, - send: function(data) { - if (!this.csrf_isPost) return this.csrf_send(data); - prepend = csrfMagicName + '=' + csrfMagicToken + '&'; + send: function(data) { + if (!this.csrf_isPost) return this.csrf_send(data); + prepend = csrfMagicName + '=' + csrfMagicToken + '&'; // XXX: Removed to eliminate 'Refused to set unsafe header "Content-length" ' errors in modern browsers // if (this.csrf_purportedLength === undefined) { // this.csrf_setRequestHeader("Content-length", this.csrf_purportedLength + prepend.length); // delete this.csrf_purportedLength; // } - delete this.csrf_isPost; - return this.csrf_send(prepend + data); - }, - csrf_send: function(data) { - return this.csrf.send(data); - }, + delete this.csrf_isPost; + return this.csrf_send(prepend + data); + }, + csrf_send: function(data) { + return this.csrf.send(data); + }, - setRequestHeader: function(header, value) { - // We have to auto-set this at the end, since we don't know how long the - // nonce is when added to the data. - if (this.csrf_isPost && header == "Content-length") { - this.csrf_purportedLength = value; - return; - } - return this.csrf_setRequestHeader(header, value); - }, - csrf_setRequestHeader: function(header, value) { - return this.csrf.setRequestHeader(header, value); - }, + setRequestHeader: function(header, value) { + // We have to auto-set this at the end, since we don't know how long the + // nonce is when added to the data. + if (this.csrf_isPost && header == "Content-length") { + this.csrf_purportedLength = value; + return; + } + return this.csrf_setRequestHeader(header, value); + }, + csrf_setRequestHeader: function(header, value) { + return this.csrf.setRequestHeader(header, value); + }, - abort: function() { - return this.csrf.abort(); - }, - getAllResponseHeaders: function() { - return this.csrf.getAllResponseHeaders(); - }, - getResponseHeader: function(header) { - return this.csrf.getResponseHeader(header); - } // , -} + abort: function() { + return this.csrf.abort(); + }, + getAllResponseHeaders: function() { + return this.csrf.getAllResponseHeaders(); + }, + getResponseHeader: function(header) { + return this.csrf.getResponseHeader(header); + } // , +}; // proprietary CsrfMagic.prototype._updateProps = function() { - this.readyState = this.csrf.readyState; - if (this.readyState == 4) { - this.responseText = this.csrf.responseText; - this.responseXML = this.csrf.responseXML; - this.status = this.csrf.status; - this.statusText = this.csrf.statusText; - } -} + this.readyState = this.csrf.readyState; + if (this.readyState == 4) { + this.responseText = this.csrf.responseText; + this.responseXML = this.csrf.responseXML; + this.status = this.csrf.status; + this.statusText = this.csrf.statusText; + } +}; + CsrfMagic.process = function(base) { - if(typeof base == 'object') { - base[csrfMagicName] = csrfMagicToken; - return base; - } - var prepend = csrfMagicName + '=' + csrfMagicToken; - if (base) return prepend + '&' + base; - return prepend; -} + if ( typeof base == 'object' ) { + base[csrfMagicName] = csrfMagicToken; + return base; + } + var prepend = csrfMagicName + '=' + csrfMagicToken; + if ( base ) return prepend + '&' + base; + return prepend; +}; + // callback function for when everything on the page has loaded CsrfMagic.end = function() { - // This rewrites forms AGAIN, so in case buffering didn't work this - // certainly will. - forms = document.getElementsByTagName('form'); - for (var i = 0; i < forms.length; i++) { - form = forms[i]; - if (form.method.toUpperCase() !== 'POST') continue; - if (form.elements[csrfMagicName]) continue; - var input = document.createElement('input'); - input.setAttribute('name', csrfMagicName); - input.setAttribute('value', csrfMagicToken); - input.setAttribute('type', 'hidden'); - form.appendChild(input); - } -} + // This rewrites forms AGAIN, so in case buffering didn't work this + // certainly will. + forms = document.getElementsByTagName('form'); + for (var i = 0; i < forms.length; i++) { + form = forms[i]; + if (form.method.toUpperCase() !== 'POST') continue; + if (form.elements[csrfMagicName]) continue; + var input = document.createElement('input'); + input.setAttribute('name', csrfMagicName); + input.setAttribute('value', csrfMagicToken); + input.setAttribute('type', 'hidden'); + form.appendChild(input); + } +}; // Sets things up for Mozilla/Opera/nice browsers // We very specifically match against Internet Explorer, since they haven't // implemented prototypes correctly yet. -if (window.XMLHttpRequest && window.XMLHttpRequest.prototype && '\v' != 'v') { - var x = XMLHttpRequest.prototype; - var c = CsrfMagic.prototype; +if ( window.XMLHttpRequest && window.XMLHttpRequest.prototype && '\v' != 'v' ) { + var x = XMLHttpRequest.prototype; + var c = CsrfMagic.prototype; - // Save the original functions - x.csrf_open = x.open; - x.csrf_send = x.send; - x.csrf_setRequestHeader = x.setRequestHeader; + // Save the original functions + x.csrf_open = x.open; + x.csrf_send = x.send; + x.csrf_setRequestHeader = x.setRequestHeader; - // Notice that CsrfMagic is itself an instantiatable object, but only - // open, send and setRequestHeader are necessary as decorators. - x.open = c.open; - x.send = c.send; - x.setRequestHeader = c.setRequestHeader; + // Notice that CsrfMagic is itself an instantiatable object, but only + // open, send and setRequestHeader are necessary as decorators. + x.open = c.open; + x.send = c.send; + x.setRequestHeader = c.setRequestHeader; } else { - // The only way we can do this is by modifying a library you have been - // using. We support YUI, script.aculo.us, prototype, MooTools, - // jQuery, Ext and Dojo. - if (window.jQuery) { - // jQuery didn't implement a new XMLHttpRequest function, so we have - // to do this the hard way. - jQuery.csrf_ajax = jQuery.ajax; - jQuery.ajax = function( s ) { - if (s.type && s.type.toUpperCase() == 'POST') { - s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s)); - if ( s.data && s.processData && typeof s.data != "string" ) { - s.data = jQuery.param(s.data); - } - s.data = CsrfMagic.process(s.data); - } - return jQuery.csrf_ajax( s ); + // The only way we can do this is by modifying a library you have been + // using. We support YUI, script.aculo.us, prototype, MooTools, + // jQuery, Ext and Dojo. + if ( window.jQuery ) { + // jQuery didn't implement a new XMLHttpRequest function, so we have + // to do this the hard way. + jQuery.csrf_ajax = jQuery.ajax; + jQuery.ajax = function( s ) { + if (s.type && s.type.toUpperCase() == 'POST') { + s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s)); + if ( s.data && s.processData && typeof s.data != "string" ) { + s.data = jQuery.param(s.data); } - } - if (window.Prototype) { - // This works for script.aculo.us too - Ajax.csrf_getTransport = Ajax.getTransport; - Ajax.getTransport = function() { - return new CsrfMagic(Ajax.csrf_getTransport()); - } - } - if (window.MooTools) { - Browser.csrf_Request = Browser.Request; - Browser.Request = function () { - return new CsrfMagic(Browser.csrf_Request()); - } - } - if (window.YAHOO) { - // old YUI API - YAHOO.util.Connect.csrf_createXhrObject = YAHOO.util.Connect.createXhrObject; - YAHOO.util.Connect.createXhrObject = function (transaction) { - obj = YAHOO.util.Connect.csrf_createXhrObject(transaction); - obj.conn = new CsrfMagic(obj.conn); - return obj; - } - } - if (window.Ext) { - // Ext can use other js libraries as loaders, so it has to come last - // Ext's implementation is pretty identical to Yahoo's, but we duplicate - // it for comprehensiveness's sake. - Ext.lib.Ajax.csrf_createXhrObject = Ext.lib.Ajax.createXhrObject; - Ext.lib.Ajax.createXhrObject = function (transaction) { - obj = Ext.lib.Ajax.csrf_createXhrObject(transaction); - obj.conn = new CsrfMagic(obj.conn); - return obj; - } - } - if (window.dojo) { - // NOTE: this doesn't work with latest dojo - dojo.csrf__xhrObj = dojo._xhrObj; - dojo._xhrObj = function () { - return new CsrfMagic(dojo.csrf__xhrObj()); - } - } -} + s.data = CsrfMagic.process(s.data); + } + return jQuery.csrf_ajax(s); + }; + } + if ( window.Prototype ) { + // This works for script.aculo.us too + Ajax.csrf_getTransport = Ajax.getTransport; + Ajax.getTransport = function() { + return new CsrfMagic(Ajax.csrf_getTransport()); + }; + } + if ( window.MooTools ) { + Browser.csrf_Request = Browser.Request; + Browser.Request = function() { + return new CsrfMagic(Browser.csrf_Request()); + }; + } + if ( window.YAHOO ) { + // old YUI API + YAHOO.util.Connect.csrf_createXhrObject = YAHOO.util.Connect.createXhrObject; + YAHOO.util.Connect.createXhrObject = function(transaction) { + obj = YAHOO.util.Connect.csrf_createXhrObject(transaction); + obj.conn = new CsrfMagic(obj.conn); + return obj; + }; + } + if ( window.Ext ) { + // Ext can use other js libraries as loaders, so it has to come last + // Ext's implementation is pretty identical to Yahoo's, but we duplicate + // it for comprehensiveness's sake. + Ext.lib.Ajax.csrf_createXhrObject = Ext.lib.Ajax.createXhrObject; + Ext.lib.Ajax.createXhrObject = function(transaction) { + obj = Ext.lib.Ajax.csrf_createXhrObject(transaction); + obj.conn = new CsrfMagic(obj.conn); + return obj; + }; + } + if ( window.dojo ) { + // NOTE: this doesn't work with latest dojo + dojo.csrf__xhrObj = dojo._xhrObj; + dojo._xhrObj = function() { + return new CsrfMagic(dojo.csrf__xhrObj()); + }; + } +}; diff --git a/web/includes/csrf/csrf-magic.php b/web/includes/csrf/csrf-magic.php index 55819329c..d96165eb9 100644 --- a/web/includes/csrf/csrf-magic.php +++ b/web/includes/csrf/csrf-magic.php @@ -150,24 +150,27 @@ function csrf_ob_handler($buffer, $flags) { return $buffer; } } + global $cspNonce; $tokens = csrf_get_tokens(); $name = $GLOBALS['csrf']['input-name']; $endslash = $GLOBALS['csrf']['xhtml'] ? ' /' : ''; $input = ""; $buffer = preg_replace('#(]*method\s*=\s*["\']post["\'][^>]*>)#i', '$1' . $input, $buffer); if ($GLOBALS['csrf']['frame-breaker']) { - $buffer = str_ireplace('', '', $buffer); + $buffer = str_ireplace('', ' +', $buffer); } if ($js = $GLOBALS['csrf']['rewrite-js']) { $buffer = str_ireplace( '', - ''. - '', + ' + ', $buffer ); - $script = ''; + $script = ''; $buffer = str_ireplace('', $script . '', $buffer, $count); if (!$count) { $buffer .= $script; @@ -183,6 +186,7 @@ function csrf_ob_handler($buffer, $flags) { */ function csrf_check($fatal = true) { if ($_SERVER['REQUEST_METHOD'] !== 'POST') return true; + global $cspNonce; csrf_start(); $name = $GLOBALS['csrf']['input-name']; $ok = false; @@ -207,6 +211,7 @@ break; } $ok = true; } while (false); + if ($fatal && !$ok) { $callback = $GLOBALS['csrf']['callback']; if (trim($tokens, 'A..Za..z0..9:;,') !== '') $tokens = 'hidden'; @@ -286,9 +291,14 @@ function csrf_callback($tokens) { echo "CSRF check failed

CSRF check failed. Your form session may have expired, or you may not have - cookies enabled.

-
$data
-

Debug: $tokens

+ cookies enabled.

"; + if (ZM_LOG_DEBUG) { + // Don't make it too easy for users to inflict a CSRF attack on themselves. + echo "

Only try again if you weren't sent to this page by someone as this is potentially a sign of an attack.

"; + echo "
$data
"; + ZM\Logger::Debug("Failed csrf check"); + } + echo "

Debug: $tokens

"; } diff --git a/web/includes/database.php b/web/includes/database.php index 2c3a818c1..d0124656d 100644 --- a/web/includes/database.php +++ b/web/includes/database.php @@ -2,25 +2,25 @@ // // ZoneMinder web database interface file, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes -// +// // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// +// -define( 'DB_LOG_OFF', 0 ); -define( 'DB_LOG_ONLY', 1 ); -define( 'DB_LOG_DEBUG', 2 ); +define('DB_LOG_OFF', 0); +define('DB_LOG_ONLY', 1); +define('DB_LOG_DEBUG', 2); $GLOBALS['dbLogLevel'] = DB_LOG_OFF; @@ -29,10 +29,10 @@ $GLOBALS['dbConn'] = false; function dbConnect() { global $dbConn; - if (strpos(ZM_DB_HOST, ':')) { + if ( strpos(ZM_DB_HOST, ':') ) { // Host variable may carry a port or socket. list($host, $portOrSocket) = explode(':', ZM_DB_HOST, 2); - if (ctype_digit($portOrSocket)) { + if ( ctype_digit($portOrSocket) ) { $socket = ':host='.$host . ';port='.$portOrSocket; } else { $socket = ':unix_socket='.$portOrSocket; @@ -43,22 +43,22 @@ function dbConnect() { try { $dbOptions = null; - if ( defined( 'ZM_DB_SSL_CA_CERT' ) and ZM_DB_SSL_CA_CERT ) { + if ( defined('ZM_DB_SSL_CA_CERT') and ZM_DB_SSL_CA_CERT ) { $dbOptions = array( PDO::MYSQL_ATTR_SSL_CA => ZM_DB_SSL_CA_CERT, PDO::MYSQL_ATTR_SSL_KEY => ZM_DB_SSL_CLIENT_KEY, PDO::MYSQL_ATTR_SSL_CERT => ZM_DB_SSL_CLIENT_CERT, ); - $dbConn = new PDO( ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS, $dbOptions ); + $dbConn = new PDO(ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS, $dbOptions); } else { - $dbConn = new PDO( ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS ); + $dbConn = new PDO(ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS); } $dbConn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $dbConn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - } catch(PDOException $ex ) { + } catch(PDOException $ex) { echo 'Unable to connect to ZM db.' . $ex->getMessage(); - error_log('Unable to connect to ZM DB ' . $ex->getMessage() ); + error_log('Unable to connect to ZM DB ' . $ex->getMessage()); $dbConn = null; } } @@ -89,187 +89,197 @@ function dbDebug() { dbLogDebug(); } -function dbLog( $sql, $update=false ) { +function dbLog($sql, $update=false) { global $dbLogLevel; $noExecute = $update && ($dbLogLevel >= DB_LOG_DEBUG); if ( $dbLogLevel > DB_LOG_OFF ) - Logger::Debug( "SQL-LOG: $sql".($noExecute?" (not executed)":"") ); + ZM\Logger::Debug( "SQL-LOG: $sql".($noExecute?' (not executed)':'') ); return( $noExecute ); } -function dbError( $sql ) { - Error( "SQL-ERR '".$dbConn->errorInfo()."', statement was '".$sql."'" ); +function dbError($sql) { + global $dbConn; + $error = $dbConn->errorInfo(); + if ( ! $error[0] ) + return ''; + + $message = "SQL-ERR '".implode("\n",$dbConn->errorInfo())."', statement was '".$sql."'"; + ZM\Error($message); + return $message; } function dbEscape( $string ) { global $dbConn; - if ( version_compare( phpversion(), '4.3.0', '<') ) - if ( get_magic_quotes_gpc() ) - return( $dbConn->quote( stripslashes( $string ) ) ); - else - return( $dbConn->quote( $string ) ); + if ( version_compare(phpversion(), '5.4', '<=') and get_magic_quotes_gpc() ) + return $dbConn->quote(stripslashes($string)); else - if ( get_magic_quotes_gpc() ) - return( $dbConn->quote( stripslashes( $string ) ) ); - else - return( $dbConn->quote( $string ) ); + return $dbConn->quote($string); } -function dbQuery( $sql, $params=NULL ) { +function dbQuery($sql, $params=NULL) { global $dbConn; - if ( dbLog( $sql, true ) ) + if ( dbLog($sql, true) ) return; $result = NULL; try { if ( isset($params) ) { - if ( ! $result = $dbConn->prepare( $sql ) ) { - Error("SQL: Error preparing $sql: " . $pdo->errorInfo); + if ( ! $result = $dbConn->prepare($sql) ) { + ZM\Error("SQL: Error preparing $sql: " . $pdo->errorInfo); return NULL; } - if ( ! $result->execute( $params ) ) { - Error("SQL: Error executing $sql: " . implode(',', $result->errorInfo() ) ); + if ( ! $result->execute($params) ) { + ZM\Error("SQL: Error executing $sql: " . print_r($result->errorInfo(), true)); return NULL; } } else { if ( defined('ZM_DB_DEBUG') ) { - Logger::Debug("SQL: $sql values:" . ($params?implode(',',$params):'') ); + ZM\Logger::Debug("SQL: $sql values:" . ($params?implode(',',$params):'')); } $result = $dbConn->query($sql); + if ( ! $result ) { + ZM\Error("SQL: Error preparing $sql: " . $pdo->errorInfo); + return NULL; + } } if ( defined('ZM_DB_DEBUG') ) { if ( $params ) - Warning("SQL: $sql" . implode(',',$params) . ' rows: '.$result->rowCount() ); + ZM\Logger::Debug("SQL: $sql " . implode(',',$params) . ' rows: '.$result->rowCount()); else - Warning("SQL: $sql: rows:" . $result->rowCount() ); + ZM\Logger::Debug("SQL: $sql: rows:" . $result->rowCount()); } } catch(PDOException $e) { - Error( "SQL-ERR '".$e->getMessage()."', statement was '".$sql."' params:" . ($params?implode(',',$params):'') ); + ZM\Error("SQL-ERR '".$e->getMessage()."', statement was '".$sql."' params:" . ($params?implode(',',$params):'')); return NULL; } return $result; } -function dbFetchOne( $sql, $col=false, $params=NULL ) { - $result = dbQuery( $sql, $params ); - if ( ! $result ) { - Error( "SQL-ERR dbFetchOne no result, statement was '".$sql."'" . ( $params ? 'params: ' . join(',',$params) : '' ) ); +function dbFetchOne($sql, $col=false, $params=NULL) { + $result = dbQuery($sql, $params); + if ( !$result ) { + ZM\Error("SQL-ERR dbFetchOne no result, statement was '".$sql."'".($params ? 'params: ' . join(',',$params) : '')); return false; } - if ( ! $result->rowCount() ) { + if ( !$result->rowCount() ) { # No rows is not an error return false; } - if ( $result && $dbRow = $result->fetch(PDO::FETCH_ASSOC) ) { + if ( $result && ($dbRow = $result->fetch(PDO::FETCH_ASSOC)) ) { if ( $col ) { if ( ! array_key_exists($col, $dbRow) ) { - Warning("$col does not exist in the returned row " . print_r($dbRow, true)); + ZM\Warning("$col does not exist in the returned row " . print_r($dbRow, true)); + return false; } return $dbRow[$col]; - } + } return $dbRow; } return false; } -function dbFetchAll( $sql, $col=false, $params=NULL ) { - $result = dbQuery( $sql, $params ); +function dbFetchAll($sql, $col=false, $params=NULL) { + $result = dbQuery($sql, $params); if ( ! $result ) { - Error( "SQL-ERR dbFetchAll no result, statement was '".$sql."'" . ( $params ? 'params: ' .join(',', $params) : '' ) ); + ZM\Error("SQL-ERR dbFetchAll no result, statement was '".$sql."'".($params ? 'params: '.join(',', $params) : '')); return false; } $dbRows = array(); - while( $dbRow = $result->fetch( PDO::FETCH_ASSOC ) ) - $dbRows[] = $col?$dbRow[$col]:$dbRow; + while ( $dbRow = $result->fetch(PDO::FETCH_ASSOC) ) + $dbRows[] = $col ? $dbRow[$col] : $dbRow; return $dbRows; } -function dbFetchAssoc( $sql, $indexCol, $dataCol=false ) { - $result = dbQuery( $sql ); +function dbFetchAssoc($sql, $indexCol, $dataCol=false) { + $result = dbQuery($sql); $dbRows = array(); - while( $dbRow = $result->fetch( PDO::FETCH_ASSOC ) ) - $dbRows[$dbRow[$indexCol]] = $dataCol?$dbRow[$dataCol]:$dbRow; - return( $dbRows ); + while( $dbRow = $result->fetch(PDO::FETCH_ASSOC) ) + $dbRows[$dbRow[$indexCol]] = $dataCol ? $dbRow[$dataCol] : $dbRow; + return $dbRows; } -function dbFetch( $sql, $col=false ) { - return( dbFetchAll( $sql, $col ) ); +function dbFetch($sql, $col=false) { + return dbFetchAll($sql, $col); } -function dbFetchNext( $result, $col=false ) { - if ( $dbRow = $result->fetch( PDO::FETCH_ASSOC ) ) - return( $col?$dbRow[$col]:$dbRow ); - return( false ); +function dbFetchNext($result, $col=false) { + if ( !$result ) { + ZM\Error("dbFetchNext called on null result."); + return false; + } + if ( $dbRow = $result->fetch(PDO::FETCH_ASSOC) ) + return $col ? $dbRow[$col] : $dbRow; + return false; } function dbNumRows( $sql ) { - $result = dbQuery( $sql ); - return( $result->rowCount() ); + $result = dbQuery($sql); + return $result->rowCount(); } function dbInsertId() { global $dbConn; - return( $dbConn->lastInsertId() ); + return $dbConn->lastInsertId(); } -function getEnumValues( $table, $column ) { - $row = dbFetchOne( "describe $table $column" ); - preg_match_all( "/'([^']+)'/", $row['Type'], $matches ); - return( $matches[1] ); +function getEnumValues($table, $column) { + $row = dbFetchOne("DESCRIBE `$table` `$column`"); + preg_match_all("/'([^']+)'/", $row['Type'], $matches); + return $matches[1]; } -function getSetValues( $table, $column ) { - return( getEnumValues( $table, $column ) ); +function getSetValues($table, $column) { + return getEnumValues($table, $column); } -function getUniqueValues( $table, $column, $asString=1 ) { +function getUniqueValues($table, $column, $asString=1) { $values = array(); - $sql = "select distinct $column from $table where (not isnull($column) and $column != '') order by $column"; - foreach( dbFetchAll( $sql ) as $row ) { + $sql = "SELECT DISTINCT `$column` FROM `$table` WHERE (NOT isnull(`$column`) AND `$column` != '') ORDER BY `$column`"; + foreach ( dbFetchAll($sql) as $row ) { if ( $asString ) $values[$row[$column]] = $row[$column]; else $values[] = $row[$column]; } - return( $values ); -} + return $values; +} function getTableColumns( $table, $asString=1 ) { $columns = array(); - $sql = "describe $table"; - foreach( dbFetchAll( $sql ) as $row ) { + $sql = "DESCRIBE `$table`"; + foreach ( dbFetchAll($sql) as $row ) { if ( $asString ) $columns[$row['Field']] = $row['Type']; else $columns[] = $row['Type']; } - return( $columns ); -} + return $columns; +} function getTableAutoInc( $table ) { - $row = dbFetchOne( 'show table status where Name=?', NULL, array($table) ); - return( $row['Auto_increment'] ); + $row = dbFetchOne('SHOW TABLE status WHERE Name=?', NULL, array($table)); + return $row['Auto_increment']; } function getTableDescription( $table, $asString=1 ) { $columns = array(); - foreach( dbFetchAll( "describe $table" ) as $row ) { + foreach( dbFetchAll("DESCRIBE `$table`") as $row ) { $desc = array( 'name' => $row['Field'], 'required' => ($row['Null']=='NO')?true:false, 'default' => $row['Default'], 'db' => $row, ); - if ( preg_match( "/^varchar\((\d+)\)$/", $row['Type'], $matches ) ) { + if ( preg_match('/^varchar\((\d+)\)$/', $row['Type'], $matches) ) { $desc['type'] = 'text'; $desc['typeAttrib'] = 'varchar'; $desc['maxLength'] = $matches[1]; - } elseif ( preg_match( "/^(\w+)?text$/", $row['Type'], $matches ) ) { + } elseif ( preg_match('/^(\w+)?text$/', $row['Type'], $matches) ) { $desc['type'] = 'text'; - if (!empty($matches[1]) ) + if ( !empty($matches[1]) ) $desc['typeAttrib'] = $matches[1]; switch ( $matches[1] ) { case 'tiny' : @@ -283,15 +293,15 @@ function getTableDescription( $table, $asString=1 ) { //$desc['minLength'] = -128; break; default : - Error( "Unexpected text qualifier '".$matches[1]."' found for field '".$row['Field']."' in table '".$table."'" ); + ZM\Error("Unexpected text qualifier '".$matches[1]."' found for field '".$row['Field']."' in table '".$table."'"); break; } - } elseif ( preg_match( "/^(enum|set)\((.*)\)$/", $row['Type'], $matches ) ) { + } elseif ( preg_match('/^(enum|set)\((.*)\)$/', $row['Type'], $matches) ) { $desc['type'] = 'text'; $desc['typeAttrib'] = $matches[1]; - preg_match_all( "/'([^']+)'/", $matches[2], $matches ); + preg_match_all("/'([^']+)'/", $matches[2], $matches); $desc['values'] = $matches[1]; - } elseif ( preg_match( "/^(\w+)?int\(\d+\)(?:\s+(unsigned))?$/", $row['Type'], $matches ) ) { + } elseif ( preg_match('/^(\w+)?int\(\d+\)(?:\s+(unsigned))?$/', $row['Type'], $matches) ) { $desc['type'] = 'integer'; switch ( $matches[1] ) { case 'tiny' : @@ -315,7 +325,7 @@ function getTableDescription( $table, $asString=1 ) { //$desc['maxValue'] = 127; break; default : - Error( "Unexpected integer qualifier '".$matches[1]."' found for field '".$row['Field']."' in table '".$table."'" ); + ZM\Error("Unexpected integer qualifier '".$matches[1]."' found for field '".$row['Field']."' in table '".$table."'"); break; } if ( !empty($matches[1]) ) @@ -324,7 +334,7 @@ function getTableDescription( $table, $asString=1 ) { $desc['maxValue'] += (-$desc['minValue']); $desc['minValue'] = 0; } - } elseif ( preg_match( "/^(?:decimal|numeric)\((\d+)(?:,(\d+))?\)(?:\s+(unsigned))?$/", $row['Type'], $matches ) ) { + } elseif ( preg_match('/^(?:decimal|numeric)\((\d+)(?:,(\d+))?\)(?:\s+(unsigned))?$/', $row['Type'], $matches) ) { $desc['type'] = 'fixed'; $desc['range'] = $matches[1]; if ( isset($matches[2]) ) @@ -332,7 +342,7 @@ function getTableDescription( $table, $asString=1 ) { else $desc['precision'] = 0; $desc['unsigned'] = ( isset($matches[3]) && $matches[3] == 'unsigned' ); - } elseif ( preg_match( "/^(datetime|timestamp|date|time)$/", $row['Type'], $matches ) ) { + } elseif ( preg_match('/^(datetime|timestamp|date|time)$/', $row['Type'], $matches) ) { $desc['type'] = 'datetime'; switch ( $desc['typeAttrib'] = $matches[1] ) { case 'datetime' : @@ -350,7 +360,7 @@ function getTableDescription( $table, $asString=1 ) { break; } } else { - Error( "Can't parse database type '".$row['Type']."' found for field '".$row['Field']."' in table '".$table."'" ); + ZM\Error("Can't parse database type '".$row['Type']."' found for field '".$row['Field']."' in table '".$table."'"); } if ( $asString ) @@ -358,15 +368,6 @@ function getTableDescription( $table, $asString=1 ) { else $columns[] = $desc; } - return( $columns ); -} - -function dbFetchMonitor( $mid ) { - return( dbFetchOne( 'select * from Monitors where Id = ?', NULL, array($mid) ) ); + return $columns; } - -function dbFetchGroup( $gid ) { - return( dbFetchOne( 'select * from Groups where Id = ?', NULL, array($gid) ) ); -} - ?> diff --git a/web/includes/functions.php b/web/includes/functions.php index 0600d4f60..8086e77df 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -19,11 +19,11 @@ // // Compatibility functions -if ( version_compare( phpversion(), '4.3.0', '<') ) { +if ( version_compare(phpversion(), '4.3.0', '<') ) { function ob_get_clean() { $buffer = ob_get_contents(); ob_end_clean(); - return( $buffer ); + return $buffer; } } @@ -35,81 +35,117 @@ function noCacheHeaders() { header('Pragma: no-cache'); // HTTP/1.0 } -function CORSHeaders() { - if ( isset( $_SERVER['HTTP_ORIGIN'] ) ) { - -# The following is left for future reference/use. - $valid = false; - $servers = dbFetchAll( 'SELECT * FROM Servers' ); - if ( sizeof($servers) <= 1 ) { -# Only need CORSHeaders in the event that there are multiple servers in use. - return; - } - foreach( $servers as $row ) { - $Server = new Server( $row ); - if ( $_SERVER['HTTP_ORIGIN'] == $Server->Url() ) { - $valid = true; - header('Access-Control-Allow-Origin: ' . $Server->Url() ); - header('Access-Control-Allow-Headers: x-requested-with,x-request'); +function CSPHeaders($view, $nonce) { + $additionalScriptSrc = ''; + switch ($view) { + case 'login': { + if (defined('ZM_OPT_USE_GOOG_RECAPTCHA') + && defined('ZM_OPT_GOOG_RECAPTCHA_SITEKEY') + && defined('ZM_OPT_GOOG_RECAPTCHA_SECRETKEY') + && ZM_OPT_USE_GOOG_RECAPTCHA && ZM_OPT_GOOG_RECAPTCHA_SITEKEY && ZM_OPT_GOOG_RECAPTCHA_SECRETKEY) { + $additionalScriptSrc = "https://www.google.com"; } + // fall through } - if ( ! $valid ) { - Warning( $_SERVER['HTTP_ORIGIN'] . ' is not found in servers list.' ); + case 'bandwidth': + case 'blank': + case 'console': + case 'controlcap': + case 'cycle': + case 'donate': + case 'download': + case 'error': + case 'events': + case 'export': + case 'frame': + case 'function': + case 'log': + case 'logout': + case 'optionhelp': + case 'options': + case 'plugin': + case 'postlogin': + case 'privacy': + case 'server': + case 'state': + case 'status': + case 'storage': + case 'version': { + // Enforce script-src on pages where inline scripts and event handlers have been fixed. + // 'unsafe-inline' is only for backwards compatibility with browsers which + // only support CSP 1 (with no nonce-* support). + header("Content-Security-Policy: script-src 'unsafe-inline' 'self' 'nonce-$nonce' $additionalScriptSrc"); + break; + } + default: { + // Use Report-Only mode on all other pages. + header("Content-Security-Policy-Report-Only: script-src 'unsafe-inline' 'self' 'nonce-$nonce' $additionalScriptSrc;". + (ZM_CSP_REPORT_URI ? ' report-uri '.ZM_CSP_REPORT_URI : '' ) + ); + break; } } } -function getStreamSrc( $args, $querySep='&' ) { - $streamSrc = ZM_BASE_URL.ZM_PATH_ZMS; +function CORSHeaders() { + if ( isset($_SERVER['HTTP_ORIGIN']) ) { - if ( ZM_OPT_USE_AUTH ) { - if ( ZM_AUTH_RELAY == 'hashed' ) { - $args[] = 'auth='.generateAuthHash( ZM_AUTH_HASH_IPS ); - } elseif ( ZM_AUTH_RELAY == 'plain' ) { - $args[] = 'user='.$_SESSION['username']; - $args[] = 'pass='.$_SESSION['password']; - } elseif ( ZM_AUTH_RELAY == 'none' ) { - $args[] = 'user='.$_SESSION['username']; +# The following is left for future reference/use. + $valid = false; + $Servers = ZM\Server::find(); + if ( sizeof($Servers) < 1 ) { +# Only need CORSHeaders in the event that there are multiple servers in use. + # ICON: Might not be true. multi-port? + if ( ZM_MIN_STREAMING_PORT ) { + ZM\Logger::Debug('Setting default Access-Control-Allow-Origin from ' . $_SERVER['HTTP_ORIGIN']); + header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']); + header('Access-Control-Allow-Headers: x-requested-with,x-request'); + } + return; + } + foreach ( $Servers as $Server ) { + if ( + preg_match('/^(https?:\/\/)?'.preg_quote($Server->Hostname(),'/').'/i', $_SERVER['HTTP_ORIGIN']) + or + preg_match('/^(https?:\/\/)?'.preg_quote($Server->Name(),'/').'/i', $_SERVER['HTTP_ORIGIN']) + ) { + $valid = true; + ZM\Logger::Debug('Setting Access-Control-Allow-Origin from '.$_SERVER['HTTP_ORIGIN']); + header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']); + header('Access-Control-Allow-Headers: x-requested-with,x-request'); + break; + } + } + if ( !$valid ) { + ZM\Warning($_SERVER['HTTP_ORIGIN'] . ' is not found in servers list.'); } } - if ( !in_array( 'mode=single', $args ) && !empty($GLOBALS['connkey']) ) { - $args[] = 'connkey='.$GLOBALS['connkey']; - } - if ( ZM_RAND_STREAM ) { - $args[] = 'rand='.time(); - } - - if ( count($args) ) { - $streamSrc .= '?'.join( $querySep, $args ); - } - - return( $streamSrc ); } function getMimeType( $file ) { if ( function_exists('mime_content_type') ) { - return( mime_content_type( $file ) ); + return mime_content_type($file); } elseif ( function_exists('finfo_file') ) { - $finfo = finfo_open( FILEINFO_MIME ); - $mimeType = finfo_file( $finfo, $file ); + $finfo = finfo_open(FILEINFO_MIME); + $mimeType = finfo_file($finfo, $file); finfo_close($finfo); - return( $mimeType ); + return $mimeType; } - return( trim( exec( 'file -bi '.escapeshellarg( $file ).' 2>/dev/null' ) ) ); + return trim(exec('file -bi '.escapeshellarg($file).' 2>/dev/null')); } -function outputVideoStream( $id, $src, $width, $height, $format, $title='' ) { - echo getVideoStreamHTML( $id, $src, $width, $height, $format, $title ); +function outputVideoStream($id, $src, $width, $height, $format, $title='') { + echo getVideoStreamHTML($id, $src, $width, $height, $format, $title); } -function getVideoStreamHTML( $id, $src, $width, $height, $format, $title='' ) { +function getVideoStreamHTML($id, $src, $width, $height, $format, $title='') { $html = ''; $width = validInt($width); $height = validInt($height); $title = validHtmlStr($title); - if ( file_exists( $src ) ) { - $mimeType = getMimeType( $src ); + if ( file_exists($src) ) { + $mimeType = getMimeType($src); } else { switch( $format ) { case 'asf' : @@ -144,7 +180,6 @@ function getVideoStreamHTML( $id, $src, $width, $height, $format, $title='' ) { case 'video/x-ms-asf' : case 'video/x-msvideo' : case 'video/mp4' : - { if ( isWindows() ) { return ' '; } - } case 'video/quicktime' : - { return ' '; - } case 'application/x-shockwave-flash' : - { return ' '; - } } # end switch } # end if use object tags return ''; } else { - return ''. validHtmlStr($title) .''; + return ''. validHtmlStr($title) .''; } } -function outputControlStream( $src, $width, $height, $monitor, $scale, $target ) { +function outputControlStream($src, $width, $height, $monitor, $scale, $target) { ?> -
- - - + + + + - + - + - + - +
'; } -function outputImageStill( $id, $src, $width, $height, $title='' ) { - echo getImageStill( $id, $src, $width, $height, $title='' ); +function outputImageStill($id, $src, $width, $height, $title='') { + echo getImageStill($id, $src, $width, $height, $title=''); } -function getImageStill( $id, $src, $width, $height, $title='' ) { - return ''.$title.''; +function getImageStill($id, $src, $width, $height, $title='') { + return ''.$title.''; } -function getWebSiteUrl( $id, $src, $width, $height, $title='' ) { - # Prevent unsightly warnings when php cannot verify the ssl certificate - stream_context_set_default( [ - 'ssl' => [ - 'verify_peer' => false, - 'verify_peer_name' => false, - ], - ]); - # The End User can turn off the following warning under Options -> Web - if ( ZM_WEB_XFRAME_WARN ) { - $header = get_headers($src, 1); - # If the target website has set X-Frame-Options, check it for "sameorigin" and warn the end user - if (array_key_exists('X-Frame-Options', $header)) { - $header = $header['X-Frame-Options']; - if ( stripos($header, 'sameorigin') === 0 ) - Warning("Web site $src has X-Frame-Options set to sameorigin. An X-Frame-Options browser plugin is required to display this site."); - } +function getWebSiteUrl($id, $src, $width, $height, $title='') { + # Prevent unsightly warnings when php cannot verify the ssl certificate + stream_context_set_default( [ + 'ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + ], + ]); + # The End User can turn off the following warning under Options -> Web + if ( ZM_WEB_XFRAME_WARN ) { + $header = get_headers($src, 1); + # If the target website has set X-Frame-Options, check it for "sameorigin" and warn the end user + if ( array_key_exists('X-Frame-Options', $header) ) { + $header = $header['X-Frame-Options']; + if ( stripos($header, 'sameorigin') === 0 ) + ZM\Warning("Web site $src has X-Frame-Options set to sameorigin. An X-Frame-Options browser plugin is required to display this site."); } - return ''; + } + return ''; } -function outputControlStill( $src, $width, $height, $monitor, $scale, $target ) { +function outputControlStill($src, $width, $height, $monitor, $scale, $target) { ?> -
- - - + + + + - + - + - + - - + +
getStreamSrc( array( 'mode'=>'mpeg', 'format'=>'h264' ) ); +function getEventDefaultVideoPath($event) { + $Event = new ZM\Event($event); + return $Event->getStreamSrc(array('mode'=>'mpeg', 'format'=>'h264')); } function deletePath( $path ) { - if ( is_dir( $path ) ) { - system( escapeshellcmd( 'rm -rf '.$path ) ); + ZM\Logger::Debug("Deleting $path"); + if ( is_dir($path) ) { + system(escapeshellcmd('rm -rf '.$path)); } else if ( file_exists($path) ) { - unlink( $path ); + unlink($path); } } -function deleteEvent( $event ) { +function deleteEvent($event) { if ( empty($event) ) { - Error( 'Empty event passed to deleteEvent.'); + ZM\Error('Empty event passed to deleteEvent.'); return; } if ( gettype($event) != 'array' ) { # $event could be an eid, so turn it into an event hash - $event = new Event( $event ); + $event = new ZM\Event($event); } else { -Logger::Debug("Event type: " . gettype($event)); +ZM\Logger::Debug("Event type: " . gettype($event)); } global $user; + if ( $event->Archived() ) { + ZM\Info('Cannot delete Archived event.'); + return; + } # end if Archived + if ( $user['Events'] == 'Edit' ) { $event->delete(); } # CAN EDIT } -function makeLink( $url, $label, $condition=1, $options='' ) { +function makeLink($url, $label, $condition=1, $options='') { $string = ''; if ( $condition ) { $string .= ''; @@ -390,37 +428,53 @@ function makeLink( $url, $label, $condition=1, $options='' ) { if ( $condition ) { $string .= ''; } - return( $string ); + return $string; } -function makePopupLink( $url, $winName, $winSize, $label, $condition=1, $options='' ) { - $string = ''; +/** + * $label must be already escaped. It can't be done here since it sometimes contains HTML tags. + */ +function makePopupLink($url, $winName, $winSize, $label, $condition=1, $options='') { + // Avoid double-encoding since some consumers incorrectly pass a pre-escaped URL. + $string = ''; + $string .= ($options ? (' ' . $options ) : '') . '>'; } else { $string .= ''; } $string .= $label; $string .= ''; - return( $string ); + return $string; } -function makePopupButton( $url, $winName, $winSize, $buttonValue, $condition=1, $options='' ) { - if ( is_array( $winSize ) ) - $popupParms = "'".$url."', '".$winName."', '".$winSize[0]."', ".$winSize[1].", ".$winSize[2]; - else - $popupParms = "'".$url."', '".$winName."', '".$winSize."'"; - $string = ''; - return( $string ); +function makePopupButton($url, $winName, $winSize, $buttonValue, $condition=1, $options='') { + $string = ''; + return $string; } -function htmlSelect( $name, $contents, $values, $behaviours=false ) { - +function htmlSelect($name, $contents, $values, $behaviours=false) { $behaviourText = ''; if ( !empty($behaviours) ) { if ( is_array($behaviours) ) { @@ -432,36 +486,53 @@ function htmlSelect( $name, $contents, $values, $behaviours=false ) { } } - return "'; + return "'; } -function htmlOptions( $contents, $values ) { - $html = ''; - foreach ( $contents as $value=>$text ) { - if ( is_array( $text ) ) - $text = $text['Name']; - else if ( is_object( $text ) ) - $text = $text->Name(); - $selected = is_array( $values ) ? in_array( $value, $values ) : !strcmp($value, $values); - $html .= ""; +function htmlOptions($contents, $values) { + $options_html = ''; + + foreach ( $contents as $value=>$option ) { + $disabled = 0; + $text = ''; + if ( is_array($option) ) { + + if ( isset($option['Name']) ) + $text = $option['Name']; + else if ( isset($option['text']) ) + $text = $option['text']; + + if ( isset($option['disabled']) ) { + $disabled = $option['disabled']; + } + } else if ( is_object($option) ) { + $text = $option->Name(); + } else { + $text = $option; + } + $selected = is_array($values) ? in_array($value, $values) : !strcmp($value, $values); + $options_html .= ''; } - return $html; + return $options_html; } -function truncText( $text, $length, $deslash=1 ) { - return( preg_replace( '/^(.{'.$length.',}?)\b.*$/', '\\1…', ($deslash?stripslashes($text):$text) ) ); -} +function truncText($text, $length, $deslash=1) { + return preg_replace('/^(.{'.$length.',}?)\b.*$/', '\\1…', ($deslash?stripslashes($text):$text)); +} -function buildSelect( $name, $contents, $behaviours=false ) { +function buildSelect($name, $contents, $behaviours=false) { $value = ''; - if ( preg_match( '/^\s*(\w+)\s*(\[.*\])?\s*$/', $name, $matches ) && count($matches) > 2 ) { + if ( preg_match('/^\s*(\w+)\s*(\[.*\])?\s*$/', $name, $matches) && (count($matches) > 2) ) { $arr = $matches[1]; if ( isset($GLOBALS[$arr]) ) $value = $GLOBALS[$arr]; elseif ( isset($_REQUEST[$arr]) ) $value = $_REQUEST[$arr]; - if ( !preg_match_all( '/\[\s*[\'"]?(\w+)["\']?\s*\]/', $matches[2], $matches ) ) { - Fatal( "Can't parse selector '$name'" ); + if ( !preg_match_all('/\[\s*[\'"]?(\w+)["\']?\s*\]/', $matches[2], $matches) ) { + ZM\Fatal("Can't parse selector '$name'"); } for ( $i = 0; $i < count($matches[1]); $i++ ) { $idx = $matches[1][$i]; @@ -498,10 +569,10 @@ function buildSelect( $name, $contents, $behaviours=false ) { $html = ob_get_contents(); ob_end_clean(); - return( $html ); + return $html; } -function getFormChanges( $values, $newValues, $types=false, $columns=false ) { +function getFormChanges($values, $newValues, $types=false, $columns=false) { $changes = array(); if ( !$types ) $types = array(); @@ -515,7 +586,6 @@ function getFormChanges( $values, $newValues, $types=false, $columns=false ) { switch( $types[$key] ) { case 'set' : - { if ( is_array($newValues[$key]) ) { if ( (!isset($values[$key])) or ( join(',',$newValues[$key]) != $values[$key] ) ) { $changes[$key] = "`$key` = ".dbEscape(join(',',$newValues[$key])); @@ -524,9 +594,7 @@ function getFormChanges( $values, $newValues, $types=false, $columns=false ) { $changes[$key] = "`$key` = ''"; } break; - } case 'image' : - { if ( is_array( $newValues[$key] ) ) { $imageData = getimagesize( $newValues[$key]['tmp_name'] ); $changes[$key.'Width'] = $key.'Width = '.$imageData[0]; @@ -541,9 +609,7 @@ function getFormChanges( $values, $newValues, $types=false, $columns=false ) { $changes[$key] = "$key = ".dbEscape($value); } break; - } case 'document' : - { if ( is_array( $newValues[$key] ) ) { $imageData = getimagesize( $newValues[$key]['tmp_name'] ); $changes[$key.'Type'] = $key."Type = '".$newValues[$key]['type']."'"; @@ -556,9 +622,7 @@ function getFormChanges( $values, $newValues, $types=false, $columns=false ) { $changes[$key] = $key . ' = '.dbEscape($value); } break; - } case 'file' : - { $changes[$key.'Type'] = $key.'Type = '.dbEscape($newValues[$key]['type']); $changes[$key.'Size'] = $key.'Size = '.dbEscape($newValues[$key]['size']); ob_start(); @@ -566,14 +630,11 @@ function getFormChanges( $values, $newValues, $types=false, $columns=false ) { $changes[$key] = $key." = '".dbEscape( ob_get_contents() )."'"; ob_end_clean(); break; - } case 'raw' : - { if ( (!isset($values[$key])) or ($values[$key] != $value) ) { $changes[$key] = $key . ' = '.dbEscape($value); } break; - } case 'toggle' : if ( (!isset($values[$key])) or $values[$key] != $value ) { if ( empty($value) ) { @@ -584,8 +645,12 @@ function getFormChanges( $values, $newValues, $types=false, $columns=false ) { } } break; + case 'integer' : + if ( (!isset($values[$key])) or $values[$key] != $value ) { + $changes[$key] = $key . ' = '.intval($value); + } + break; default : - { if ( !isset($values[$key]) || ($values[$key] != $value) ) { if ( ! isset($value) || $value == '' ) { $changes[$key] = "`$key` = NULL"; @@ -594,10 +659,9 @@ function getFormChanges( $values, $newValues, $types=false, $columns=false ) { } } break; - } } // end switch } // end foreach newvalues - foreach( $values as $key=>$value ) { + foreach ( $values as $key=>$value ) { if ( !empty($columns[$key]) ) { if ( !empty($types[$key]) ) { if ( $types[$key] == 'toggle' ) { @@ -610,18 +674,22 @@ function getFormChanges( $values, $newValues, $types=false, $columns=false ) { } } } - return( $changes ); + return $changes; } -function getBrowser( &$browser, &$version ) { +function getBrowser(&$browser, &$version) { if ( isset($_SESSION['browser']) ) { $browser = $_SESSION['browser']; $version = $_SESSION['version']; } else { - if (( preg_match( '/MSIE (.*?);/', $_SERVER['HTTP_USER_AGENT'], $logVersion)) || (preg_match( '/.*Trident.*rv:(.*?)(;|\))/', $_SERVER['HTTP_USER_AGENT'], $logVersion))) { + if ( + ( preg_match('/MSIE (.*?);/', $_SERVER['HTTP_USER_AGENT'], $logVersion)) + || + ( preg_match('/.*Trident.*rv:(.*?)(;|\))/', $_SERVER['HTTP_USER_AGENT'], $logVersion)) + ) { $version = $logVersion[1]; $browser = 'ie'; - } elseif ( preg_match( '/Chrome\/([0-9.]+)/', $_SERVER['HTTP_USER_AGENT'], $logVersion) ) { + } else if ( preg_match('/Chrome\/([0-9.]+)/', $_SERVER['HTTP_USER_AGENT'], $logVersion) ) { $version = $logVersion[1]; // Check for old version of Chrome with bug 5876 if ( $version < 7 ) { @@ -629,16 +697,16 @@ function getBrowser( &$browser, &$version ) { } else { $browser = 'chrome'; } - } elseif ( preg_match( '/Safari\/([0-9.]+)/', $_SERVER['HTTP_USER_AGENT'], $logVersion) ) { + } else if ( preg_match('/Safari\/([0-9.]+)/', $_SERVER['HTTP_USER_AGENT'], $logVersion) ) { $version = $logVersion[1]; $browser = 'safari'; - } elseif ( preg_match( '/Opera[ \/]([0-9].[0-9]{1,2})/', $_SERVER['HTTP_USER_AGENT'], $logVersion) ) { + } else if ( preg_match('/Opera[ \/]([0-9].[0-9]{1,2})/', $_SERVER['HTTP_USER_AGENT'], $logVersion) ) { $version = $logVersion[1]; $browser = 'opera'; - } elseif ( preg_match( '/Konqueror\/([0-9.]+)/', $_SERVER['HTTP_USER_AGENT'], $logVersion) ) { + } else if ( preg_match('/Konqueror\/([0-9.]+)/', $_SERVER['HTTP_USER_AGENT'], $logVersion) ) { $version = $logVersion[1]; $browser = 'konqueror'; - } elseif ( preg_match( '/Mozilla\/([0-9].[0-9]{1,2})/', $_SERVER['HTTP_USER_AGENT'], $logVersion) ) { + } else if ( preg_match('/Mozilla\/([0-9].[0-9]{1,2})/', $_SERVER['HTTP_USER_AGENT'], $logVersion) ) { $version = $logVersion[1]; $browser = 'mozilla'; } else { @@ -651,53 +719,53 @@ function getBrowser( &$browser, &$version ) { } function isMozilla() { - getBrowser( $browser, $version ); + getBrowser($browser, $version); - return( $browser == 'mozilla' ); + return $browser == 'mozilla'; } function isKonqueror() { - getBrowser( $browser, $version ); + getBrowser($browser, $version); - return( $browser == 'konqueror' ); + return $browser == 'konqueror'; } function isInternetExplorer() { - getBrowser( $browser, $version ); + getBrowser($browser, $version); - return( $browser == 'ie' ); + return $browser == 'ie'; } function isOldChrome() { - getBrowser( $browser, $version ); + getBrowser($browser, $version); - return( $browser == 'oldchrome' ); + return $browser == 'oldchrome'; } function isChrome() { - getBrowser( $browser, $version ); + getBrowser($browser, $version); - return( $browser == 'chrome' ); + return $browser == 'chrome'; } function isOpera() { - getBrowser( $browser, $version ); + getBrowser($browser, $version); - return( $browser == 'opera' ); + return $browser == 'opera'; } function isSafari() { - getBrowser( $browser, $version ); + getBrowser($browser, $version); - return( $browser == 'safari' ); + return $browser == 'safari'; } function isWindows() { - return ( preg_match( '/Win/', $_SERVER['HTTP_USER_AGENT'] ) ); + return preg_match('/Win/', $_SERVER['HTTP_USER_AGENT']); } function canStreamIframe() { - return( isKonqueror() ); + return isKonqueror(); } function canStreamNative() { @@ -707,23 +775,23 @@ function canStreamNative() { function canStreamApplet() { if ( (ZM_OPT_CAMBOZOLA && !file_exists( ZM_PATH_WEB.'/'.ZM_PATH_CAMBOZOLA )) ) { - Warning ( 'ZM_OPT_CAMBOZOLA is enabled, but the system cannot find '.ZM_PATH_WEB.'/'.ZM_PATH_CAMBOZOLA ); + ZM\Warning('ZM_OPT_CAMBOZOLA is enabled, but the system cannot find '.ZM_PATH_WEB.'/'.ZM_PATH_CAMBOZOLA); } - return( (ZM_OPT_CAMBOZOLA && file_exists( ZM_PATH_WEB.'/'.ZM_PATH_CAMBOZOLA )) ); + return (ZM_OPT_CAMBOZOLA && file_exists(ZM_PATH_WEB.'/'.ZM_PATH_CAMBOZOLA)); } function canStream() { - return( canStreamNative() | canStreamApplet() ); + return canStreamNative() | canStreamApplet(); } -function packageControl( $command ) { - $string = ZM_PATH_BIN.'/zmpkg.pl '.escapeshellarg( $command ); +function packageControl($command) { + $string = ZM_PATH_BIN.'/zmpkg.pl '.escapeshellarg($command); $string .= ' 2>/dev/null >&- <&- >/dev/null'; - exec( $string ); + exec($string); } -function daemonControl( $command, $daemon=false, $args=false ) { +function daemonControl($command, $daemon=false, $args=false) { $string = escapeshellcmd(ZM_PATH_BIN).'/zmdc.pl '.$command; if ( $daemon ) { $string .= ' ' . $daemon; @@ -731,19 +799,19 @@ function daemonControl( $command, $daemon=false, $args=false ) { $string .= ' ' . $args; } } - $string = escapeshellcmd( $string ); + $string = escapeshellcmd($string); #$string .= ' 2>/dev/null >&- <&- >/dev/null'; -Logger::Debug("daemonControl $string"); - exec( $string ); + ZM\Logger::Debug("daemonControl $string"); + exec($string); } function zmcControl($monitor, $mode=false) { - $Monitor = new Monitor( $monitor ); + $Monitor = new ZM\Monitor($monitor); return $Monitor->zmcControl($mode); } function zmaControl($monitor, $mode=false) { - $Monitor = new Monitor($monitor); + $Monitor = new ZM\Monitor($monitor); return $Monitor->zmaControl($mode); } @@ -752,15 +820,15 @@ function initDaemonStatus() { if ( !isset($daemon_status) ) { if ( daemonCheck() ) { - $string = ZM_PATH_BIN."/zmdc.pl status"; - $daemon_status = shell_exec( $string ); + $string = ZM_PATH_BIN.'/zmdc.pl status'; + $daemon_status = shell_exec($string); } else { $daemon_status = ''; } } } -function daemonStatus( $daemon, $args=false ) { +function daemonStatus($daemon, $args=false) { global $daemon_status; initDaemonStatus(); @@ -768,72 +836,66 @@ function daemonStatus( $daemon, $args=false ) { $string = $daemon; if ( $args ) $string .= ' ' . $args; - return( strpos( $daemon_status, "'$string' running" ) !== false ); + return( strpos($daemon_status, "'$string' running") !== false ); } -function zmcStatus( $monitor ) { +function zmcStatus($monitor) { if ( $monitor['Type'] == 'Local' ) { $zmcArgs = '-d '.$monitor['Device']; } else { $zmcArgs = '-m '.$monitor['Id']; } - return( daemonStatus( 'zmc', $zmcArgs ) ); + return daemonStatus('zmc', $zmcArgs); } -function zmaStatus( $monitor ) { - if ( is_array( $monitor ) ) { +function zmaStatus($monitor) { + if ( is_array($monitor) ) { $monitor = $monitor['Id']; } - return( daemonStatus( 'zma', "-m $monitor" ) ); + return daemonStatus('zma', "-m $monitor"); } -function daemonCheck( $daemon=false, $args=false ) { +function daemonCheck($daemon=false, $args=false) { $string = ZM_PATH_BIN.'/zmdc.pl check'; if ( $daemon ) { $string .= ' ' . $daemon; if ( $args ) $string .= ' '. $args; } - $string = escapeshellcmd( $string ); - $result = exec( $string ); - return( preg_match( '/running/', $result ) ); + $string = escapeshellcmd($string); + $result = exec($string); + return preg_match('/running/', $result); } -function zmcCheck( $monitor ) { +function zmcCheck($monitor) { if ( $monitor['Type'] == 'Local' ) { $zmcArgs = '-d '.$monitor['Device']; } else { $zmcArgs = '-m '.$monitor['Id']; } - return( daemonCheck( 'zmc', $zmcArgs ) ); + return daemonCheck('zmc', $zmcArgs); } -function zmaCheck( $monitor ) { - if ( is_array( $monitor ) ) { +function zmaCheck($monitor) { + if ( is_array($monitor) ) { $monitor = $monitor['Id']; } - return( daemonCheck( 'zma', "-m $monitor" ) ); + return daemonCheck('zma', "-m $monitor"); } -function getImageSrc( $event, $frame, $scale=SCALE_BASE, $captureOnly=false, $overwrite=false ) { - $Event = new Event( $event ); - return $Event->getImageSrc( $frame, $scale, $captureOnly, $overwrite ); +function getImageSrc($event, $frame, $scale=SCALE_BASE, $captureOnly=false, $overwrite=false) { + $Event = new ZM\Event($event); + return $Event->getImageSrc($frame, $scale, $captureOnly, $overwrite); } -function viewImagePath( $path, $querySep='&' ) { - if ( strncmp( $path, ZM_DIR_IMAGES, strlen(ZM_DIR_IMAGES) ) == 0 ) { - // Thumbnails - return( $path ); - } elseif ( strpos( ZM_DIR_EVENTS, '/' ) === 0 ) { - return( '?view=image'.$querySep.'path='.$path ); - } - return( ZM_DIR_EVENTS.'/'.$path ); +function viewImagePath($path, $querySep='&') { + return '?view=image'.$querySep.'path='.$path; } -function createListThumbnail( $event, $overwrite=false ) { +function createListThumbnail($event, $overwrite=false) { # Load the frame with the highest score to use as a thumbnail - if ( !($frame = dbFetchOne( "SELECT * FROM Frames WHERE EventId=? AND Score=? ORDER BY FrameId LIMIT 1", NULL, array( $event['Id'], $event['MaxScore'] ) )) ) - return( false ); + if ( !($frame = dbFetchOne('SELECT * FROM Frames WHERE EventId=? AND Score=? ORDER BY FrameId LIMIT 1', NULL, array($event['Id'], $event['MaxScore']) )) ) + return false; $frameId = $frame['FrameId']; @@ -846,12 +908,12 @@ function createListThumbnail( $event, $overwrite=false ) { $scale = (SCALE_BASE*ZM_WEB_LIST_THUMB_HEIGHT)/$event['Height']; $thumbWidth = reScale( $event['Width'], $scale ); } else { - Fatal( "No thumbnail width or height specified, please check in Options->Web" ); + ZM\Fatal('No thumbnail width or height specified, please check in Options->Web'); } - $imageData = getImageSrc( $event, $frame, $scale, false, $overwrite ); - if ( ! $imageData ) { - return ( false ); + $imageData = getImageSrc($event, $frame, $scale, false, $overwrite); + if ( !$imageData ) { + return false; } $thumbData = $frame; @@ -859,31 +921,24 @@ function createListThumbnail( $event, $overwrite=false ) { $thumbData['Width'] = (int)$thumbWidth; $thumbData['Height'] = (int)$thumbHeight; - return( $thumbData ); + return $thumbData; } -function createVideo( $event, $format, $rate, $scale, $overwrite=false ) { - $command = ZM_PATH_BIN."/zmvideo.pl -e ".$event['Id']." -f ".$format." -r ".sprintf( "%.2F", ($rate/RATE_BASE) ); - if ( preg_match( '/\d+x\d+/', $scale ) ) - $command .= " -S ".$scale; +function createVideo($event, $format, $rate, $scale, $overwrite=false) { + $command = ZM_PATH_BIN.'/zmvideo.pl -e '.$event['Id'].' -f '.$format.' -r '.sprintf('%.2F', ($rate/RATE_BASE)); + if ( preg_match('/\d+x\d+/', $scale) ) + $command .= ' -S '.$scale; else - if ( version_compare( phpversion(), "4.3.10", ">=") ) - $command .= " -s ".sprintf( "%.2F", ($scale/SCALE_BASE) ); + if ( version_compare(phpversion(), '4.3.10', '>=') ) + $command .= ' -s '.sprintf('%.2F', ($scale/SCALE_BASE)); else - $command .= " -s ".sprintf( "%.2f", ($scale/SCALE_BASE) ); + $command .= ' -s '.sprintf('%.2f', ($scale/SCALE_BASE)); if ( $overwrite ) - $command .= " -o"; - $command = escapeshellcmd( $command ); - $result = exec( $command, $output, $status ); + $command .= ' -o'; + $command = escapeshellcmd($command); + $result = exec($command, $output, $status); Logger::Debug("generating Video $command: result($result outptu:(".implode("\n", $output )." status($status"); - return( $status?"":rtrim($result) ); -} - -function executeFilter( $filter ) { - $command = ZM_PATH_BIN."/zmfilter.pl --filter ".escapeshellarg($filter); - $result = exec( $command, $output, $status ); - dbQuery( "delete from Filters where Name like '_TempFilter%'" ); - return( $status ); + return $status ? '' : rtrim($result); } # This takes more than one scale amount, so it runs through each and alters dimension. @@ -892,41 +947,41 @@ function reScale( $dimension, $dummy ) { $new_dimension = $dimension; for ( $i = 1; $i < func_num_args(); $i++ ) { $scale = func_get_arg( $i ); - if ( !empty($scale) && $scale != SCALE_BASE ) + if ( !empty($scale) && ($scale != 'auto') && ($scale != SCALE_BASE) ) $new_dimension = (int)(($new_dimension*$scale)/SCALE_BASE); } - return( $new_dimension ); + return $new_dimension; } -function deScale( $dimension, $dummy ) { +function deScale($dimension, $dummy) { $new_dimension = $dimension; for ( $i = 1; $i < func_num_args(); $i++ ) { - $scale = func_get_arg( $i ); + $scale = func_get_arg($i); if ( !empty($scale) && $scale != SCALE_BASE ) $new_dimension = (int)(($new_dimension*SCALE_BASE)/$scale); } - return( $new_dimension ); + return $new_dimension; } function monitorLimitSql() { global $user; if ( !empty($user['MonitorIds']) ) - $midSql = " and MonitorId in (".join( ",", preg_split( '/["\'\s]*,["\'\s]*/', $user['MonitorIds'] ) ).")"; + $midSql = ' AND MonitorId IN ('.join(',', preg_split('/["\'\s]*,["\'\s]*/', $user['MonitorIds'])).')'; else $midSql = ''; - return( $midSql ); + return $midSql; } -function parseSort( $saveToSession=false, $querySep='&' ) { +function parseSort($saveToSession=false, $querySep='&') { global $sortQuery, $sortColumn, $sortOrder, $limitQuery; // Outputs - if (isset($_REQUEST['filter']['Query']['sort_field'])) { //Handle both new and legacy filter passing + if ( isset($_REQUEST['filter']['Query']['sort_field']) ) { //Handle both new and legacy filter passing $_REQUEST['sort_field'] = $_REQUEST['filter']['Query']['sort_field']; } - if (isset($_REQUEST['filter']['Query']['sort_asc'])) { + if ( isset($_REQUEST['filter']['Query']['sort_asc']) ) { $_REQUEST['sort_asc'] = $_REQUEST['filter']['Query']['sort_asc']; } - if (isset($_REQUEST['filter']['Query']['limit'])) { + if ( isset($_REQUEST['filter']['Query']['limit']) ) { $_REQUEST['limit'] = $_REQUEST['filter']['Query']['limit']; } if ( empty($_REQUEST['sort_field']) ) { @@ -956,9 +1011,15 @@ function parseSort( $saveToSession=false, $querySep='&' ) { case 'StartTime' : $sortColumn = 'E.StartTime'; break; + case 'StartDateTime' : + $sortColumn = 'E.StartTime'; + break; case 'EndTime' : $sortColumn = 'E.EndTime'; break; + case 'EndDateTime' : + $sortColumn = 'E.EndTime'; + break; case 'Length' : $sortColumn = 'E.Length'; break; @@ -977,13 +1038,28 @@ function parseSort( $saveToSession=false, $querySep='&' ) { case 'MaxScore' : $sortColumn = 'E.MaxScore'; break; + case 'FramesFrameId' : + $sortColumn = 'F.FrameId'; + break; + case 'FramesType' : + $sortColumn = 'F.Type'; + break; + case 'FramesTimeStamp' : + $sortColumn = 'F.TimeStamp'; + break; + case 'FramesDelta' : + $sortColumn = 'F.Delta'; + break; + case 'FramesScore' : + $sortColumn = 'F.Score'; + break; default: $sortColumn = 'E.StartTime'; break; } - $sortOrder = $_REQUEST['sort_asc']?'asc':'desc'; if ( !$_REQUEST['sort_asc'] ) $_REQUEST['sort_asc'] = 0; + $sortOrder = $_REQUEST['sort_asc'] ? 'asc' : 'desc'; $sortQuery = $querySep.'sort_field='.validHtmlStr($_REQUEST['sort_field']).$querySep.'sort_asc='.validHtmlStr($_REQUEST['sort_asc']); if ( !isset($_REQUEST['limit']) ) $_REQUEST['limit'] = ''; @@ -992,37 +1068,49 @@ function parseSort( $saveToSession=false, $querySep='&' ) { $_SESSION['sort_asc'] = validHtmlStr($_REQUEST['sort_asc']); } if ($_REQUEST['limit'] != '') { - $limitQuery = "&limit=".$_REQUEST['limit']; + $limitQuery = '&limit='.validInt($_REQUEST['limit']); } } +function getFilterQueryConjunctionTypes() { + return array( + 'and' => translate('ConjAnd'), + 'or' => translate('ConjOr') + ); +} + function parseFilter(&$filter, $saveToSession=false, $querySep='&') { - $filter['query'] = ''; + $filter['query'] = ''; $filter['sql'] = ''; $filter['fields'] = ''; + $validQueryConjunctionTypes = getFilterQueryConjunctionTypes(); $StorageArea = NULL; - $terms = isset($filter['Query']) ? $filter['Query']['terms'] : NULL; + # It is not possible to pass an empty array in the url, so we have to deal with there not being a terms field. + $terms = (isset($filter['Query']) and isset($filter['Query']['terms']) and is_array($filter['Query']['terms'])) ? $filter['Query']['terms'] : array(); - if ( isset($terms) && count($terms) ) { + if ( count($terms) ) { for ( $i = 0; $i < count($terms); $i++ ) { - if ( isset($terms[$i]['cnj']) ) { - $filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][cnj]").'='.urlencode($terms[$i]['cnj']); - $filter['sql'] .= ' '.$terms[$i]['cnj'].' '; - $filter['fields'] .= "\n"; + + $term = $terms[$i]; + + if ( isset($term['cnj']) && array_key_exists($term['cnj'], $validQueryConjunctionTypes) ) { + $filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][cnj]").'='.urlencode($term['cnj']); + $filter['sql'] .= ' '.$term['cnj'].' '; + $filter['fields'] .= "\n"; } - if ( isset($terms[$i]['obr']) ) { - $filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][obr]").'='.urlencode($terms[$i]['obr']); - $filter['sql'] .= ' '.str_repeat('(', $terms[$i]['obr']).' '; - $filter['fields'] .= "\n"; + if ( isset($term['obr']) && (string)(int)$term['obr'] == $term['obr'] ) { + $filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][obr]").'='.urlencode($term['obr']); + $filter['sql'] .= ' '.str_repeat('(', $term['obr']).' '; + $filter['fields'] .= "\n"; } - if ( isset($terms[$i]['attr']) ) { - $filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][attr]").'='.urlencode($terms[$i]['attr']); - $filter['fields'] .= "\n"; - switch ( $terms[$i]['attr'] ) { + if ( isset($term['attr']) ) { + $filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][attr]").'='.urlencode($term['attr']); + $filter['fields'] .= "\n"; + switch ( $term['attr'] ) { case 'MonitorName': - $filter['sql'] .= 'M.'.preg_replace('/^Monitor/', '', $terms[$i]['attr']); + $filter['sql'] .= 'M.Name'; break; case 'ServerId': case 'MonitorServerId': @@ -1039,45 +1127,49 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') { $filter['sql'] .= 'E.StartTime'; break; case 'Date': - $filter['sql'] .= 'to_days( E.StartTime )'; + $filter['sql'] .= 'to_days(E.StartTime)'; break; case 'Time': - $filter['sql'] .= 'extract( hour_second from E.StartTime )'; + $filter['sql'] .= 'extract(hour_second FROM E.StartTime)'; break; case 'Weekday': - $filter['sql'] .= 'weekday( E.StartTime )'; + $filter['sql'] .= 'weekday(E.StartTime)'; break; # Starting Time case 'StartDateTime': $filter['sql'] .= 'E.StartTime'; break; + case 'FramesEventId': + $filter['sql'] .= 'F.EventId'; + break; case 'StartDate': - $filter['sql'] .= 'to_days( E.StartTime )'; + $filter['sql'] .= 'to_days(E.StartTime)'; break; case 'StartTime': - $filter['sql'] .= 'extract( hour_second from E.StartTime )'; + $filter['sql'] .= 'extract(hour_second FROM E.StartTime)'; break; case 'StartWeekday': - $filter['sql'] .= 'weekday( E.StartTime )'; + $filter['sql'] .= 'weekday(E.StartTime)'; break; # Ending Time case 'EndDateTime': $filter['sql'] .= 'E.EndTime'; break; case 'EndDate': - $filter['sql'] .= 'to_days( E.EndTime )'; + $filter['sql'] .= 'to_days(E.EndTime)'; break; case 'EndTime': - $filter['sql'] .= 'extract( hour_second from E.EndTime )'; + $filter['sql'] .= 'extract(hour_second FROM E.EndTime)'; break; case 'EndWeekday': - $filter['sql'] .= 'weekday( E.EndTime )'; + $filter['sql'] .= 'weekday(E.EndTime)'; break; case 'Id': case 'Name': case 'DiskSpace': case 'MonitorId': case 'StorageId': + case 'SecondaryStorageId': case 'Length': case 'Frames': case 'AlarmFrames': @@ -1088,28 +1180,40 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') { case 'Notes': case 'StateId': case 'Archived': - $filter['sql'] .= 'E.'.$terms[$i]['attr']; + $filter['sql'] .= 'E.'.$term['attr']; break; case 'DiskPercent': // Need to specify a storage area, so need to look through other terms looking for a storage area, else we default to ZM_EVENTS_PATH if ( ! $StorageArea ) { for ( $j = 0; $j < count($terms); $j++ ) { - if ( isset($terms[$j]['attr']) and $terms[$j]['attr'] == 'StorageId' and isset($terms[$j]['val']) ) { - $StorageArea = new Storage($terms[$j]['val']); + if ( + isset($terms[$j]['attr']) + and + ($terms[$j]['attr'] == 'StorageId') + and + isset($terms[$j]['val']) + ) { + $StorageArea = ZM\Storage::find_one(array('Id'=>$terms[$j]['val'])); break; } } // end foreach remaining term - if ( ! $StorageArea ) $StorageArea = new Storage(); + if ( ! $StorageArea ) $StorageArea = new ZM\Storage(); } // end no StorageArea found yet - $filter['sql'] .= getDiskPercent( $StorageArea->Path() ); + $filter['sql'] .= getDiskPercent($StorageArea->Path()); break; case 'DiskBlocks': // Need to specify a storage area, so need to look through other terms looking for a storage area, else we default to ZM_EVENTS_PATH if ( ! $StorageArea ) { for ( $j = $i; $j < count($terms); $j++ ) { - if ( isset($terms[$i]['attr']) and $terms[$i]['attr'] == 'StorageId' and isset($terms[$j]['val']) ) { - $StorageArea = new Storage($terms[$i]['val']); + if ( + isset($terms[$j]['attr']) + and + ($terms[$j]['attr'] == 'StorageId') + and + isset($terms[$j]['val']) + ) { + $StorageArea = ZM\Storage::find_one(array('Id'=>$terms[$j]['val'])); } } // end foreach remaining term } // end no StorageArea found yet @@ -1120,12 +1224,15 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') { break; } $valueList = array(); - foreach ( preg_split( '/["\'\s]*?,["\'\s]*?/', preg_replace( '/^["\']+?(.+)["\']+?$/', '$1', $terms[$i]['val'] ) ) as $value ) { - switch ( $terms[$i]['attr'] ) { + foreach ( preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $term['val'])) as $value ) { + switch ( $term['attr'] ) { case 'MonitorName': case 'Name': case 'Cause': case 'Notes': + if($term['op'] == 'LIKE' || $term['op'] == 'NOT LIKE') { + $value = '%'.$value.'%'; + } $value = dbEscape($value); break; case 'MonitorServerId': @@ -1141,7 +1248,7 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') { } break; case 'StorageId': - $StorageArea = new Storage( $value ); + $StorageArea = ZM\Storage::find_one(array('Id'=>$value)); if ( $value != 'NULL' ) $value = dbEscape($value); break; @@ -1149,19 +1256,19 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') { case 'StartDateTime': case 'EndDateTime': if ( $value != 'NULL' ) - $value = "'".strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) )."'"; + $value = '\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\''; break; case 'Date': case 'StartDate': case 'EndDate': if ( $value != 'NULL' ) - $value = "to_days( '".strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) )."' )"; + $value = 'to_days(\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; break; case 'Time': case 'StartTime': case 'EndTime': if ( $value != 'NULL' ) - $value = "extract( hour_second from '".strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) )."' )"; + $value = 'extract(hour_second from \''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; break; default : if ( $value != 'NULL' ) @@ -1169,16 +1276,18 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') { break; } $valueList[] = $value; - } + } // end foreach value - switch ( $terms[$i]['op'] ) { + switch ( $term['op'] ) { case '=' : case '!=' : case '>=' : case '>' : case '<' : case '<=' : - $filter['sql'] .= ' '.$terms[$i]['op'].' '. $value; + case 'LIKE' : + case 'NOT LIKE': + $filter['sql'] .= ' '.$term['op'].' '. $value; break; case '=~' : $filter['sql'] .= ' regexp '.$value; @@ -1188,10 +1297,10 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') { break; case '=[]' : case 'IN' : - $filter['sql'] .= ' in ('.join( ',', $valueList ).')'; + $filter['sql'] .= ' in ('.join(',', $valueList).')'; break; case '![]' : - $filter['sql'] .= ' not in ('.join( ',', $valueList ).')'; + $filter['sql'] .= ' not in ('.join(',', $valueList).')'; break; case 'IS' : if ( $value == 'Odd' ) { @@ -1206,58 +1315,73 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') { $filter['sql'] .= " IS NOT $value"; break; default: - Warning("Invalid operator in filter: " . $terms[$i]['op'] ); - } + ZM\Warning('Invalid operator in filter: ' . print_r($term['op'], true)); + } // end switch op - $filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][op]").'='.urlencode($terms[$i]['op']); - $filter['fields'] .= "\n"; - if ( isset($terms[$i]['val']) ) { - $filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][val]").'='.urlencode($terms[$i]['val']); - $filter['fields'] .= "\n"; - } - } // end foreach term - if ( isset($terms[$i]['cbr']) ) { - $filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][cbr]").'='.urlencode($terms[$i]['cbr']); - $filter['sql'] .= ' '.str_repeat( ')', $terms[$i]['cbr'] ).' '; - $filter['fields'] .= "\n"; + $filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][op]").'='.urlencode($term['op']); + $filter['fields'] .= "\n"; + if ( isset($term['val']) ) { + $filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][val]").'='.urlencode($term['val']); + $filter['fields'] .= "\n"; + } + } // end if isset($term['attr']) + if ( isset($term['cbr']) && (string)(int)$term['cbr'] == $term['cbr'] ) { + $filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][cbr]").'='.urlencode($term['cbr']); + $filter['sql'] .= ' '.str_repeat(')', $term['cbr']); + $filter['fields'] .= "\n"; } - } + } // end foreach term if ( $filter['sql'] ) - $filter['sql'] = ' and ( '.$filter['sql'].' )'; + $filter['sql'] = ' AND ( '.$filter['sql'].' )'; if ( $saveToSession ) { $_SESSION['filter'] = $filter; } - } -} + } else { + $filter['query'] = $querySep; + #.urlencode('filter[Query][terms]=[]'); + } // end if terms + + #if ( 0 ) { + #// ICON I feel like these should be here, but not yet + #if ( isset($filter['Query']['sort_field']) ) { + #$filter['sql'] .= ' ORDER BY ' . $filter['Query']['sort_field'] . ( + #( $filter['Query']['sort_asc'] ? ' ASC' : ' DESC' ) ); + #} + #if ( $filter['Query']['limit'] ) { + #$filter['sql'] .= ' LIMIT ' . validInt($filter['Query']['limit']); + #} + #} +} // end function parseFilter(&$filter, $saveToSession=false, $querySep='&') // Please note that the filter is passed in by copy, so you need to use the return value from this function. // -function addFilterTerm( $filter, $position, $term=false ) { +function addFilterTerm($filter, $position, $term=false) { if ( $position < 0 ) $position = 0; - if ( ! isset( $filter['Query']['terms'] ) ) + if ( !isset($filter['Query']['terms']) ) $filter['Query']['terms'] = array(); - elseif( $position > count($filter['Query']['terms']) ) + else if ( $position > count($filter['Query']['terms']) ) $position = count($filter['Query']['terms']); - if ( $term && $position == 0 ) - unset( $term['cnj'] ); - array_splice( $filter['Query']['terms'], $position, 0, array( $term?$term:array() ) ); - return( $filter ); + if ( $term && $position == 0 ) + unset($term['cnj']); + array_splice($filter['Query']['terms'], $position, 0, array($term ? $term : array())); + + return $filter; } -function delFilterTerm( $filter, $position ) { +function delFilterTerm($filter, $position) { if ( $position < 0 ) $position = 0; - elseif( $position >= count($filter['Query']['terms']) ) + else if ( $position >= count($filter['Query']['terms']) ) $position = count($filter['Query']['terms']); - array_splice( $filter['Query']['terms'], $position, 1 ); + array_splice($filter['Query']['terms'], $position, 1); - return( $filter ); + return $filter; } -function getPagination( $pages, $page, $maxShortcuts, $query, $querySep='&' ) { +function getPagination($pages, $page, $maxShortcuts, $query, $querySep='&') { global $view; $pageText = ''; @@ -1284,7 +1408,7 @@ function getPagination( $pages, $page, $maxShortcuts, $query, $querySep='&' if ( $newPage <= 1 ) break; $pagesUsed[$newPage] = true; - array_unshift( $newPages, $newPage ); + array_unshift($newPages, $newPage); } if ( !isset($pagesUsed[1]) ) array_unshift( $newPages, 1 ); @@ -1292,8 +1416,8 @@ function getPagination( $pages, $page, $maxShortcuts, $query, $querySep='&' foreach ( $newPages as $newPage ) { $pageText .= ''.$newPage.' '; } + } # end if page > 1 - } $pageText .= '- '.$page.' -'; if ( $page < $pages ) { $newPages = array(); @@ -1306,10 +1430,10 @@ function getPagination( $pages, $page, $maxShortcuts, $query, $querySep='&' if ( $newPage > $pages ) break; $pagesUsed[$newPage] = true; - array_push( $newPages, $newPage ); + array_push($newPages, $newPage); } if ( !isset($pagesUsed[$pages]) ) - array_push( $newPages, $pages ); + array_push($newPages, $pages); foreach ( $newPages as $newPage ) { $pageText .= ' '.$newPage.''; @@ -1318,115 +1442,122 @@ function getPagination( $pages, $page, $maxShortcuts, $query, $querySep='&' if ( false && $page < ($pages-1) ) { $pageText .= '>>'; } - } + } # end if $page < $pages } } - return( $pageText ); + return $pageText; } -function sortHeader( $field, $querySep='&' ) { +function sortHeader($field, $querySep='&') { global $view; - return( '?view='.$view.$querySep.'page=1'.$_REQUEST['filter']['query'].$querySep.'sort_field='.$field.$querySep.'sort_asc='.($_REQUEST['sort_field'] == $field?!$_REQUEST['sort_asc']:0).$querySep.'limit='.$_REQUEST['limit'] ); + return implode($querySep, array( + '?view='.$view, + 'page=1'.(isset($_REQUEST['filter'])?$_REQUEST['filter']['query']:''), + 'sort_field='.$field, + 'sort_asc='.($_REQUEST['sort_field'] == $field ? !$_REQUEST['sort_asc'] : 0), + 'limit='.validInt($_REQUEST['limit']), + (isset($_REQUEST['eid']) ? 'eid='.$_REQUEST['eid'] : '' ), + )); } function sortTag( $field ) { if ( $_REQUEST['sort_field'] == $field ) if ( $_REQUEST['sort_asc'] ) - return( '(^)' ); + return '(^)'; else - return( '(v)' ); - return( false ); + return '(v)'; + return false; } function getLoad() { $load = sys_getloadavg(); - return( $load[0] ); + return $load[0]; } function getDiskPercent($path = ZM_DIR_EVENTS) { $total = disk_total_space($path); if ( $total === false ) { - Error('disk_total_space returned false. Verify the web account user has access to ' . $path ); + Error('disk_total_space returned false. Verify the web account user has access to ' . $path); return 0; } elseif ( $total == 0 ) { - Error('disk_total_space indicates the following path has a filesystem size of zero bytes ' . $path ); + Error('disk_total_space indicates the following path has a filesystem size of zero bytes ' . $path); return 100; } $free = disk_free_space($path); if ( $free === false ) { - Error('disk_free_space returned false. Verify the web account user has access to ' . $path ); + Error('disk_free_space returned false. Verify the web account user has access to ' . $path); } $space = round((($total - $free) / $total) * 100); - return( $space ); + return $space; } function getDiskBlocks() { - if ( ! $StorageArea ) $StorageArea = new Storage(); - $df = shell_exec( 'df '.escapeshellarg($StorageArea->Path() )); + if ( !$StorageArea ) $StorageArea = new ZM\Storage(); + $df = shell_exec('df '.escapeshellarg($StorageArea->Path())); $space = -1; - if ( preg_match( '/\s(\d+)\s+\d+\s+\d+%/ms', $df, $matches ) ) + if ( preg_match('/\s(\d+)\s+\d+\s+\d+%/ms', $df, $matches) ) $space = $matches[1]; - return( $space ); + return $space; } function systemStats() { - $load = getLoad(); - $diskPercent = getDiskPercent(); - $pathMapPercent = getDiskPercent(ZM_PATH_MAP); - $cpus = getcpus(); + $load = getLoad(); + $diskPercent = getDiskPercent(); + $pathMapPercent = getDiskPercent(ZM_PATH_MAP); + $cpus = getcpus(); - $normalized_load = $load / $cpus; + $normalized_load = $load / $cpus; - # Colorize the system load stat - if ( $normalized_load <= 0.75 ) { - $htmlLoad=$load; - } elseif ( $normalized_load <= 0.9 ) { - $htmlLoad="$load"; - } elseif ( $normalized_load <= 1.1 ) { - $htmlLoad="$load"; + # Colorize the system load stat + if ( $normalized_load <= 0.75 ) { + $htmlLoad = $load; + } else if ( $normalized_load <= 0.9 ) { + $htmlLoad = "$load"; + } else if ( $normalized_load <= 1.1 ) { + $htmlLoad = "$load"; + } else { + $htmlLoad = "$load"; + } + + # Colorize the disk space stat + if ( $diskPercent < 98 ) { + $htmlDiskPercent = $diskPercent.'%'; + } else if ( $diskPercent <= 99 ) { + $htmlDiskPercent = "$diskPercent%"; + } else { + $htmlDiskPercent = "$diskPercent%"; + } + + # Colorize the PATH_MAP (usually /dev/shm) stat + if ( $pathMapPercent < 90 ) { + if ( disk_free_space(ZM_PATH_MAP) > 209715200 ) { # have to always have at least 200MiB free + $htmlPathMapPercent = $pathMapPercent.'%'; } else { - $htmlLoad="$load"; + $htmlPathMapPercent = "$pathMapPercent%"; } + } else if ( $pathMapPercent < 100 ) { + $htmlPathMapPercent = "$pathMapPercent%"; + } else { + $htmlPathMapPercent = "$pathMapPercent%"; + } - # Colorize the disk space stat - if ( $diskPercent < 98 ) { - $htmlDiskPercent="$diskPercent%"; - } elseif ( $diskPercent <= 99 ) { - $htmlDiskPercent="$diskPercent%"; - } else { - $htmlDiskPercent="$diskPercent%"; - } + $htmlString = translate('Load').": $htmlLoad - ".translate('Disk').": $htmlDiskPercent - ".ZM_PATH_MAP.": $htmlPathMapPercent"; - # Colorize the PATH_MAP (usually /dev/shm) stat - if ( $pathMapPercent < 90 ) { - if ( disk_free_space(ZM_PATH_MAP) > 209715200 ) { # have to always have at least 200MiB free - $htmlPathMapPercent="$pathMapPercent%"; - } else { - $htmlPathMapPercent="$pathMapPercent%"; - } - } elseif ( $pathMapPercent < 100 ) { - $htmlPathMapPercent="$pathMapPercent%"; - } else { - $htmlPathMapPercent="$pathMapPercent%"; - } - - $htmlString = translate('Load').": $htmlLoad - ".translate('Disk').": $htmlDiskPercent - ".ZM_PATH_MAP.": $htmlPathMapPercent"; - - return( $htmlString ); + return $htmlString; } function getcpus() { - if (is_readable("/proc/cpuinfo") ) { # Works on Linux - preg_match_all('/^processor/m', file_get_contents('/proc/cpuinfo'), $matches); - $num_cpus = count($matches[0]); - } else { # Works on BSD - $matches = explode(":", shell_exec("sysctl hw.ncpu")); - $num_cpus = trim($matches[1]); - } + if ( is_readable('/proc/cpuinfo') ) { # Works on Linux + preg_match_all('/^processor/m', file_get_contents('/proc/cpuinfo'), $matches); + $num_cpus = count($matches[0]); + } else { # Works on BSD + $matches = explode(':', shell_exec('sysctl hw.ncpu')); + $num_cpus = trim($matches[1]); + } - return( $num_cpus ); + return $num_cpus; } // Function to fix a problem whereby the built in PHP session handling @@ -1434,37 +1565,37 @@ function getcpus() { // fieldset tag, neither of which will work with strict XHTML Basic. function sidField() { if ( SID ) { - list( $sessname, $sessid ) = explode( "=", SID ); + list($sessname, $sessid) = explode('=', SID); ?> '; - return( false ); + return false; } $dx1 = $line1[1]['x'] - $line1[0]['x']; @@ -1519,54 +1650,54 @@ function linesIntersect( $line1, $line2 ) { if ( $x >= $min_x1 && $x <= $max_x1 && $x >= $min_x2 && $x <= $max_x2 ) { if ( $debug ) echo "Intersecting, at x $x
"; - return( true ); + return true; } else { if ( $debug ) echo "Not intersecting, out of range at x $x
"; - return( false ); + return false; } } elseif ( $b1 == $b2 ) { // Colinear, must overlap due to box check, intersect? if ( $debug ) echo 'Intersecting, colinear
'; - return( true ); + return true; } else { // Parallel if ( $debug ) echo 'Not intersecting, parallel
'; - return( false ); + return false; } } elseif ( !$dx1 ) { // Line 1 is vertical $y = ( $m2 * $line1[0]['x'] ) * $b2; if ( $y >= $min_y1 && $y <= $max_y1 ) { if ( $debug ) echo "Intersecting, at y $y
"; - return( true ); + return true; } else { if ( $debug ) echo "Not intersecting, out of range at y $y
"; - return( false ); + return false; } } elseif ( !$dx2 ) { // Line 2 is vertical $y = ( $m1 * $line2[0]['x'] ) * $b1; if ( $y >= $min_y2 && $y <= $max_y2 ) { if ( $debug ) echo "Intersecting, at y $y
"; - return( true ); + return true; } else { if ( $debug ) echo "Not intersecting, out of range at y $y
"; - return( false ); + return false; } } else { // Both lines are vertical if ( $line1[0]['x'] == $line2[0]['x'] ) { // Colinear, must overlap due to box check, intersect? if ( $debug ) echo 'Intersecting, vertical, colinear
'; - return( true ); + return true; } else { // Parallel if ( $debug ) echo 'Not intersecting, vertical, parallel
'; - return( false ); + return false; } } if ( $debug ) echo 'Whoops, unexpected scenario
'; - return( false ); + return false; } -function isSelfIntersecting( $points ) { +function isSelfIntersecting($points) { global $debug; $n_coords = count($points); @@ -1578,20 +1709,20 @@ function isSelfIntersecting( $points ) { for ( $i = 0; $i <= ($n_coords-2); $i++ ) { for ( $j = $i+2; $j < $n_coords+min(0,$i-1); $j++ ) { if ( $debug ) echo "Checking $i and $j
"; - if ( linesIntersect( $edges[$i], $edges[$j] ) ) { + if ( linesIntersect($edges[$i], $edges[$j]) ) { if ( $debug ) echo "Lines $i and $j intersect
"; - return( true ); + return true; } } } - return( false ); + return false; } -function getPolyCentre( $points, $area=0 ) { +function getPolyCentre($points, $area=0) { $cx = 0.0; $cy = 0.0; if ( !$area ) - $area = getPolyArea( $points ); + $area = getPolyArea($points); for ( $i = 0, $j = count($points)-1; $i < count($points); $j = $i++ ) { $ct = ($points[$i]['x'] * $points[$j]['y']) - ($points[$j]['x'] * $points[$i]['y']); $cx += ($points[$i]['x'] + $points[$j]['x']) * ct; @@ -1600,21 +1731,21 @@ function getPolyCentre( $points, $area=0 ) { $cx = intval(round(abs($cx/(6.0*$area)))); $cy = intval(round(abs($cy/(6.0*$area)))); printf( "X:%cx, Y:$cy
" ); - return( array( 'x'=>$cx, 'y'=>$cy ) ); + return array('x'=>$cx, 'y'=>$cy); } -function _CompareXY( $a, $b ) { +function _CompareXY($a, $b) { if ( $a['min_y'] == $b['min_y'] ) - return( intval($a['min_x'] - $b['min_x']) ); + return intval($a['min_x'] - $b['min_x']); else - return( intval($a['min_y'] - $b['min_y']) ); + return intval($a['min_y'] - $b['min_y']); } -function _CompareX( $a, $b ) { - return( intval($a['min_x'] - $b['min_x']) ); +function _CompareX($a, $b) { + return intval($a['min_x'] - $b['min_x']); } -function getPolyArea( $points ) { +function getPolyArea($points) { global $debug; $n_coords = count($points); @@ -1640,11 +1771,16 @@ function getPolyArea( $points ) { ); } - usort( $global_edges, '_CompareXY' ); + usort($global_edges, '_CompareXY'); if ( $debug ) { for ( $i = 0; $i < count($global_edges); $i++ ) { - printf( '%d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f
', $i, $global_edges[$i]['min_y'], $global_edges[$i]['max_y'], $global_edges[$i]['min_x'], $global_edges[$i]['_1_m'] ); + printf('%d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f
', + $i, + $global_edges[$i]['min_y'], + $global_edges[$i]['max_y'], + $global_edges[$i]['min_x'], + $global_edges[$i]['_1_m']); } } @@ -1654,18 +1790,23 @@ function getPolyArea( $points ) { do { for ( $i = 0; $i < count($global_edges); $i++ ) { if ( $global_edges[$i]['min_y'] == $y ) { - if ( $debug ) printf( 'Moving global edge
' ); + if ( $debug ) printf('Moving global edge
'); $active_edges[] = $global_edges[$i]; - array_splice( $global_edges, $i, 1 ); + array_splice($global_edges, $i, 1); $i--; } else { break; } } - usort( $active_edges, '_CompareX' ); + usort($active_edges, '_CompareX'); if ( $debug ) { for ( $i = 0; $i < count($active_edges); $i++ ) { - printf( '%d - %d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f
', $y, $i, $active_edges[$i]['min_y'], $active_edges[$i]['max_y'], $active_edges[$i]['min_x'], $active_edges[$i]['_1_m'] ); + printf('%d - %d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f
', + $y, $i, + $active_edges[$i]['min_y'], + $active_edges[$i]['max_y'], + $active_edges[$i]['min_x'], + $active_edges[$i]['_1_m']); } } $last_x = 0; @@ -1681,23 +1822,23 @@ function getPolyArea( $points ) { $parity = !$parity; $last_x = $x; } - if ( $debug ) printf( '%d: Area:%d
', $y, $row_area ); + if ( $debug ) printf('%d: Area:%d
', $y, $row_area); $y++; for ( $i = 0; $i < count($active_edges); $i++ ) { if ( $y >= $active_edges[$i]['max_y'] ) { // Or >= as per sheets - if ( $debug ) printf( 'Deleting active_edge
' ); - array_splice( $active_edges, $i, 1 ); + if ( $debug ) printf('Deleting active_edge
'); + array_splice($active_edges, $i, 1); $i--; } else { $active_edges[$i]['min_x'] += $active_edges[$i]['_1_m']; } } } while ( count($global_edges) || count($active_edges) ); - if ( $debug ) printf( 'Area:%d
', $area ); - return( $area ); + if ( $debug ) printf('Area:%d
', $area); + return $area; } -function getPolyAreaOld( $points ) { +function getPolyAreaOld($points) { $area = 0.0; $edge = 0.0; for ( $i = 0, $j = count($points)-1; $i < count($points); $j = $i++ ) { @@ -1708,215 +1849,221 @@ function getPolyAreaOld( $points ) { $edge += $trap_edge; $trap_area = ($x_diff * $y_sum ); $area += $trap_area; - printf( "%d->%d, %d-%d=%.2f, %d+%d=%.2f(%.2f), %.2f, %.2f
", i, j, $points[$i]['x'], $points[$j]['x'], $x_diff, $points[$i]['y'], $points[$j]['y'], $y_sum, $y_diff, $trap_area, $trap_edge ); + printf('%d->%d, %d-%d=%.2f, %d+%d=%.2f(%.2f), %.2f, %.2f
', + $i, $j, + $points[$i]['x'], $points[$j]['x'], + $x_diff, + $points[$i]['y'], $points[$j]['y'], + $y_sum, $y_diff, $trap_area, $trap_edge); } $edge = intval(round(abs($edge))); $area = intval(round((abs($area)+$edge)/2)); echo "E:$edge
"; echo "A:$area
"; - return( $area ); + return $area; } -function mapCoords( $a ) { - return( $a['x'].",".$a['y'] ); +function mapCoords($a) { + return $a['x'].','.$a['y']; } -function pointsToCoords( $points ) { - return( join( ' ', array_map( 'mapCoords', $points ) ) ); +function pointsToCoords($points) { + return join(' ', array_map('mapCoords', $points)); } -function coordsToPoints( $coords ) { +function coordsToPoints($coords) { $points = array(); - if ( preg_match_all( '/(\d+,\d+)+/', $coords, $matches ) ) { + if ( preg_match_all('/(\d+,\d+)+/', $coords, $matches) ) { for ( $i = 0; $i < count($matches[1]); $i++ ) { - if ( preg_match( '/(\d+),(\d+)/', $matches[1][$i], $cmatches ) ) { - $points[] = array( 'x'=>$cmatches[1], 'y'=>$cmatches[2] ); + if ( preg_match('/(\d+),(\d+)/', $matches[1][$i], $cmatches) ) { + $points[] = array('x'=>$cmatches[1], 'y'=>$cmatches[2]); } else { - echo( "Bogus coordinates '".$matches[$i]."'" ); - return( false ); + echo("Bogus coordinates '".$matches[$i]."'"); + return false; } } } else { - echo( "Bogus coordinate string '$coords'" ); - return( false ); + echo("Bogus coordinate string '$coords'"); + return false; } - return( $points ); + return $points; } -function limitPoints( &$points, $min_x, $min_y, $max_x, $max_y ) { +function limitPoints(&$points, $min_x, $min_y, $max_x, $max_y) { foreach ( $points as &$point ) { if ( $point['x'] < $min_x ) { - Logger::Debug('Limiting point x'.$point['x'].' to min_x ' . $min_x ); + ZM\Logger::Debug('Limiting point x'.$point['x'].' to min_x '.$min_x); $point['x'] = $min_x; } else if ( $point['x'] > $max_x ) { - Logger::Debug('Limiting point x'.$point['x'].' to max_x ' . $max_x ); + ZM\Logger::Debug('Limiting point x'.$point['x'].' to max_x '.$max_x); $point['x'] = $max_x; } if ( $point['y'] < $min_y ) { - Logger::Debug('Limiting point y'.$point['y'].' to min_y ' . $min_y ); + ZM\Logger::Debug('Limiting point y'.$point['y'].' to min_y '.$min_y); $point['y'] = $min_y; } else if ( $point['y'] > $max_y ) { - Logger::Debug('Limiting point y'.$point['y'].' to max_y ' . $max_y ); + ZM\Logger::Debug('Limiting point y'.$point['y'].' to max_y '.$max_y); $point['y'] = $max_y; } } // end foreach point } // end function limitPoints( $points, $min_x, $min_y, $max_x, $max_y ) -function scalePoints( &$points, $scale ) { +function scalePoints(&$points, $scale) { foreach ( $points as &$point ) { - $point['x'] = reScale( $point['x'], $scale ); - $point['y'] = reScale( $point['y'], $scale ); + $point['x'] = reScale($point['x'], $scale); + $point['y'] = reScale($point['y'], $scale); } } function getLanguages() { $langs = array(); foreach ( glob('lang/*_*.php') as $file ) { - preg_match( '/([^\/]+_.+)\.php/', $file, $matches ); + preg_match('/([^\/]+_.+)\.php/', $file, $matches); $langs[$matches[1]] = $matches[1]; } - return( $langs ); + return $langs; } -function trimString( $string, $length ) { - return( preg_replace( '/^(.{'.$length.',}?)\b.*$/', '\\1…', $string ) ); +function trimString($string, $length) { + return preg_replace('/^(.{'.$length.',}?)\b.*$/', '\\1…', $string); } -function monitorIdsToNames( $ids ) { +function monitorIdsToNames($ids) { global $mITN_monitors; if ( !$mITN_monitors ) { - $sql = 'select Id, Name from Monitors'; - foreach( dbFetchAll( $sql ) as $monitor ) { + $sql = 'SELECT Id, Name FROM Monitors'; + foreach ( dbFetchAll($sql) as $monitor ) { $mITN_monitors[$monitor['Id']] = $monitor; } } $names = array(); if ( ! is_array($ids) ) { - $ids = preg_split( '/\s*,\s*/', $ids ); + $ids = preg_split('/\s*,\s*/', $ids); } foreach ( $ids as $id ) { - if ( visibleMonitor( $id ) ) { + if ( visibleMonitor($id) ) { if ( isset($mITN_monitors[$id]) ) { $names[] = $mITN_monitors[$id]['Name']; } } } - $name_string = join( ', ', $names ); - return( $name_string ); + $name_string = join(', ', $names); + return $name_string; } function initX10Status() { global $x10_status; if ( !isset($x10_status) ) { - $socket = socket_create( AF_UNIX, SOCK_STREAM, 0 ); + $socket = socket_create(AF_UNIX, SOCK_STREAM, 0); if ( $socket < 0 ) { - Fatal( 'socket_create() failed: '.socket_strerror($socket) ); + ZM\Fatal('socket_create() failed: '.socket_strerror($socket)); } $sock_file = ZM_PATH_SOCKS.'/zmx10.sock'; - if ( @socket_connect( $socket, $sock_file ) ) { + if ( @socket_connect($socket, $sock_file) ) { $command = 'status'; - if ( !socket_write( $socket, $command ) ) { - Fatal( "Can't write to control socket: ".socket_strerror(socket_last_error($socket)) ); + if ( !socket_write($socket, $command) ) { + ZM\Fatal("Can't write to control socket: ".socket_strerror(socket_last_error($socket))); } - socket_shutdown( $socket, 1 ); + socket_shutdown($socket, 1); $x10Output = ''; - while ( $x10Response = socket_read( $socket, 256 ) ) { + while ( $x10Response = socket_read($socket, 256) ) { $x10Output .= $x10Response; } - socket_close( $socket ); + socket_close($socket); } else { // Can't connect so use script - $command = ZM_PATH_BIN."/zmx10.pl --command status"; + $command = ZM_PATH_BIN.'/zmx10.pl --command status'; //$command .= " 2>/dev/null >&- <&- >/dev/null"; - $x10Output = exec( escapeshellcmd( $command ) ); + $x10Output = exec(escapeshellcmd($command)); } - foreach ( explode( "\n", $x10Output ) as $x10Response ) { - if ( preg_match( "/^(\d+)\s+(.+)$/", $x10Response, $matches ) ) { + foreach ( explode("\n", $x10Output) as $x10Response ) { + if ( preg_match('/^(\d+)\s+(.+)$/', $x10Response, $matches) ) { $x10_status[$matches[1]] = $matches[2]; } } } } -function getDeviceStatusX10( $key ) { +function getDeviceStatusX10($key) { global $x10_status; initX10Status(); if ( empty($x10_status[$key]) || !($status = $x10_status[$key]) ) $status = 'unknown'; - return( $status ); + return $status; } -function setDeviceStatusX10( $key, $status ) { - $socket = socket_create( AF_UNIX, SOCK_STREAM, 0 ); +function setDeviceStatusX10($key, $status) { + $socket = socket_create(AF_UNIX, SOCK_STREAM, 0); if ( $socket < 0 ) { - Fatal( 'socket_create() failed: '.socket_strerror($socket) ); + ZM\Fatal( 'socket_create() failed: '.socket_strerror($socket) ); } $sock_file = ZM_PATH_SOCKS.'/zmx10.sock'; - if ( @socket_connect( $socket, $sock_file ) ) { + if ( @socket_connect($socket, $sock_file) ) { $command = "$status;$key"; - if ( !socket_write( $socket, $command ) ) { - Fatal( "Can't write to control socket: ".socket_strerror(socket_last_error($socket)) ); + if ( !socket_write($socket, $command) ) { + ZM\Fatal('Can\'t write to control socket: '.socket_strerror(socket_last_error($socket))); } - socket_shutdown( $socket, 1 ); - $x10Response = socket_read( $socket, 256 ); - socket_close( $socket ); + socket_shutdown($socket, 1); + $x10Response = socket_read($socket, 256); + socket_close($socket); } else { // Can't connect so use script - $command = ZM_PATH_BIN.'/zmx10.pl --command '.escapeshellarg( $status ); + $command = ZM_PATH_BIN.'/zmx10.pl --command '.escapeshellarg($status); $command .= ' --unit-code '.escapeshellarg( $key ); //$command .= " 2>/dev/null >&- <&- >/dev/null"; - $x10Response = exec( $command ); + $x10Response = exec($command); } - if ( preg_match( '/^'.$key.'\s+(.*)/', $x10Response, $matches ) ) + if ( preg_match('/^'.$key.'\s+(.*)/', $x10Response, $matches) ) $status = $matches[1]; else $status = 'unknown'; - return( $status ); + return $status; } function logState() { $state = 'ok'; $levelCounts = array( - Logger::FATAL => array( ZM_LOG_ALERT_FAT_COUNT, ZM_LOG_ALARM_FAT_COUNT ), - Logger::ERROR => array( ZM_LOG_ALERT_ERR_COUNT, ZM_LOG_ALARM_ERR_COUNT ), - Logger::WARNING => array( ZM_LOG_ALERT_WAR_COUNT, ZM_LOG_ALARM_WAR_COUNT ), + ZM\Logger::FATAL => array( ZM_LOG_ALERT_FAT_COUNT, ZM_LOG_ALARM_FAT_COUNT ), + ZM\Logger::ERROR => array( ZM_LOG_ALERT_ERR_COUNT, ZM_LOG_ALARM_ERR_COUNT ), + ZM\Logger::WARNING => array( ZM_LOG_ALERT_WAR_COUNT, ZM_LOG_ALARM_WAR_COUNT ), ); # This is an expensive request, as it has to hit every row of the Logs Table - $sql = 'SELECT Level, COUNT(Level) AS LevelCount FROM Logs WHERE Level < '.Logger::INFO.' AND TimeKey > unix_timestamp(now() - interval '.ZM_LOG_CHECK_PERIOD.' second) GROUP BY Level ORDER BY Level ASC'; - $counts = dbFetchAll( $sql ); - - foreach ( $counts as $count ) { - if ( $count['Level'] <= Logger::PANIC ) - $count['Level'] = Logger::FATAL; - if ( !($levelCount = $levelCounts[$count['Level']]) ) { - Error( "Unexpected Log level ".$count['Level'] ); - next; - } - if ( $levelCount[1] && $count['LevelCount'] >= $levelCount[1] ) { - $state = 'alarm'; - break; - } elseif ( $levelCount[0] && $count['LevelCount'] >= $levelCount[0] ) { - $state = 'alert'; + $sql = 'SELECT Level, COUNT(Level) AS LevelCount FROM Logs WHERE Level < '.ZM\Logger::INFO.' AND TimeKey > unix_timestamp(now() - interval '.ZM_LOG_CHECK_PERIOD.' second) GROUP BY Level ORDER BY Level ASC'; + $counts = dbFetchAll($sql); + if ( $counts ) { + foreach ( $counts as $count ) { + if ( $count['Level'] <= ZM\Logger::PANIC ) + $count['Level'] = ZM\Logger::FATAL; + if ( !($levelCount = $levelCounts[$count['Level']]) ) { + Error('Unexpected Log level '.$count['Level']); + next; + } + if ( $levelCount[1] && $count['LevelCount'] >= $levelCount[1] ) { + $state = 'alarm'; + break; + } elseif ( $levelCount[0] && $count['LevelCount'] >= $levelCount[0] ) { + $state = 'alert'; + } } } - return( $state ); + return $state; } -function isVector ( &$array ) { +function isVector(&$array) { $next_key = 0; foreach ( array_keys($array) as $key ) { - if ( !is_int( $key ) ) - return( false ); + if ( !is_int($key) ) + return false; if ( $key != $next_key++ ) - return( false ); + return false; } - return( true ); + return true; } function checkJsonError($value) { @@ -1924,62 +2071,62 @@ function checkJsonError($value) { $value = var_export($value,true); switch( json_last_error() ) { case JSON_ERROR_DEPTH : - Fatal( "Unable to decode JSON string '$value', maximum stack depth exceeded" ); + ZM\Fatal("Unable to decode JSON string '$value', maximum stack depth exceeded"); case JSON_ERROR_CTRL_CHAR : - Fatal( "Unable to decode JSON string '$value', unexpected control character found" ); + ZM\Fatal("Unable to decode JSON string '$value', unexpected control character found"); case JSON_ERROR_STATE_MISMATCH : - Fatal( "Unable to decode JSON string '$value', invalid or malformed JSON" ); + ZM\Fatal("Unable to decode JSON string '$value', invalid or malformed JSON"); case JSON_ERROR_SYNTAX : - Fatal( "Unable to decode JSON string '$value', syntax error" ); + ZM\Fatal("Unable to decode JSON string '$value', syntax error"); default : - Fatal( "Unable to decode JSON string '$value', unexpected error ".json_last_error() ); + ZM\Fatal("Unable to decode JSON string '$value', unexpected error ".json_last_error()); case JSON_ERROR_NONE: break; } } } -function jsonEncode( &$value ) { +function jsonEncode(&$value) { if ( function_exists('json_encode') ) { $string = json_encode( $value ); checkJsonError($value); - return( $string ); + return $string; } switch ( gettype($value) ) { case 'double': case 'integer': - return( $value ); + return $value; case 'boolean': - return( $value?'true':'false' ); + return $value ? 'true' : 'false'; case 'string': - return( '"'.preg_replace( "/\r?\n/", '\\n', addcslashes($value,'"\\/') ).'"' ); + return '"'.preg_replace("/\r?\n/", '\\n', addcslashes($value,'"\\/')).'"'; case 'NULL': - return( 'null' ); + return 'null'; case 'object': - return( '"Object '.addcslashes(get_class($value),'"\\/').'"' ); + return '"Object '.addcslashes(get_class($value),'"\\/').'"'; case 'array': if ( isVector( $value ) ) - return( '['.join( ',', array_map( 'jsonEncode', $value) ).']' ); + return '['.join(',', array_map('jsonEncode', $value)).']'; else { $result = '{'; foreach ($value as $subkey => $subvalue ) { if ( $result != '{' ) $result .= ','; - $result .= '"'.$subkey.'":'.jsonEncode( $subvalue ); + $result .= '"'.$subkey.'":'.jsonEncode($subvalue); } - return( $result.'}' ); + return $result.'}'; } default: - return( '"'.addcslashes(gettype($value),'"\\/').'"' ); + return '"'.addcslashes(gettype($value),'"\\/').'"'; } } -function jsonDecode( $value ) { +function jsonDecode($value) { if ( function_exists('json_decode') ) { - $object = json_decode( $value, true ); + $object = json_decode($value, true); checkJsonError($value); - return( $object ); + return $object; } $comment = false; @@ -2011,83 +2158,83 @@ function jsonDecode( $value ) { $comment = !$comment; } } - eval( $out.';' ); - return( $result ); + eval($out.';'); + return $result; } -define( 'HTTP_STATUS_OK', 200 ); -define( 'HTTP_STATUS_BAD_REQUEST', 400 ); -define( 'HTTP_STATUS_FORBIDDEN', 403 ); +define('HTTP_STATUS_OK', 200); +define('HTTP_STATUS_BAD_REQUEST', 400); +define('HTTP_STATUS_FORBIDDEN', 403); -function ajaxError( $message, $code=HTTP_STATUS_OK ) { - Error( $message ); - if ( function_exists( 'ajaxCleanup' ) ) +function ajaxError($message, $code=HTTP_STATUS_OK) { + ZM\Error($message); + if ( function_exists('ajaxCleanup') ) ajaxCleanup(); if ( $code == HTTP_STATUS_OK ) { - $response = array( 'result'=>'Error', 'message'=>$message ); - header( 'Content-type: text/plain' ); - exit( jsonEncode( $response ) ); + $response = array('result'=>'Error', 'message'=>$message); + header('Content-type: text/plain'); + exit(jsonEncode($response)); } - header( "HTTP/1.0 $code $message" ); + header("HTTP/1.0 $code $message"); exit(); } -function ajaxResponse( $result=false ) { - if ( function_exists( 'ajaxCleanup' ) ) +function ajaxResponse($result=false) { + if ( function_exists('ajaxCleanup') ) ajaxCleanup(); - $response = array( 'result'=>'Ok' ); - if ( is_array( $result ) ) { - $response = array_merge( $response, $result ); - } elseif ( !empty($result) ) { + $response = array('result'=>'Ok'); + if ( is_array($result) ) { + $response = array_merge($response, $result); + } else if ( !empty($result) ) { $response['message'] = $result; } - header( 'Content-type: text/plain' ); - exit( jsonEncode( $response ) ); + header('Content-type: text/plain'); + exit(jsonEncode($response)); } function generateConnKey() { - return( rand( 1, 999999 ) ); + return rand(1, 999999); } -function detaintPath( $path ) { +function detaintPath($path) { // Remove any absolute paths, or relative ones that want to go up - $path = preg_replace( '/\.(?:\.+[\\/][\\/]*)+/', '', $path ); - $path = preg_replace( '/^[\\/]+/', '', $path ); - return( $path ); + $path = preg_replace('/\.(?:\.+[\\/][\\/]*)+/', '', $path); + $path = preg_replace('/^[\\/]+/', '', $path); + return $path; } -function cache_bust( $file ) { +function cache_bust($file) { # Use the last modified timestamp to create a link that gets a different filename # To defeat caching. Should probably use md5 hash $parts = pathinfo($file); global $css; - $dirname = preg_replace( '/\//', '_', $parts['dirname'] ); + $dirname = preg_replace('/\//', '_', $parts['dirname']); $cacheFile = $dirname.'_'.$parts['filename'].'-'.$css.'-'.filemtime($file).'.'.$parts['extension']; if ( file_exists(ZM_DIR_CACHE.'/'.$cacheFile) or symlink(ZM_PATH_WEB.'/'.$file, ZM_DIR_CACHE.'/'.$cacheFile) ) { - return '/zm/cache/'.$cacheFile; + return 'cache/'.$cacheFile; } else { - Warning("Failed linking $file to $cacheFile"); + ZM\Warning("Failed linking $file to $cacheFile"); } return $file; } -function getSkinFile( $file ) { +function getSkinFile($file) { global $skinBase; $skinFile = false; foreach ( $skinBase as $skin ) { - $tempSkinFile = detaintPath( 'skins'.'/'.$skin.'/'.$file ); - if ( file_exists( $tempSkinFile ) ) + $tempSkinFile = detaintPath('skins'.'/'.$skin.'/'.$file); + if ( file_exists($tempSkinFile) ) $skinFile = $tempSkinFile; } - return $skinFile; + return $skinFile; } -function getSkinIncludes( $file, $includeBase=false, $asOverride=false ) { +function getSkinIncludes($file, $includeBase=false, $asOverride=false) { global $skinBase; $skinFile = false; foreach ( $skinBase as $skin ) { - $tempSkinFile = detaintPath( 'skins'.'/'.$skin.'/'.$file ); - if ( file_exists( $tempSkinFile ) ) + $tempSkinFile = detaintPath('skins'.'/'.$skin.'/'.$file); + if ( file_exists($tempSkinFile) ) $skinFile = $tempSkinFile; } $includeFiles = array(); @@ -2102,50 +2249,49 @@ function getSkinIncludes( $file, $includeBase=false, $asOverride=false ) { if ( $skinFile ) $includeFiles[] = $skinFile; } - return( $includeFiles ); + return $includeFiles; } -function requestVar( $name, $default='' ) { - return( isset($_REQUEST[$name])?validHtmlStr($_REQUEST[$name]):$default ); +function requestVar($name, $default='') { + return isset($_REQUEST[$name]) ? validHtmlStr($_REQUEST[$name]) : $default; } // For numbers etc in javascript or tags etc -function validInt( $input ) { - return( preg_replace( '/\D/', '', $input ) ); +function validInt($input) { + return preg_replace('/\D/', '', $input); } function validNum( $input ) { - return( preg_replace( '/[^\d.-]/', '', $input ) ); + return preg_replace('/[^\d.-]/', '', $input); } // For general strings -function validStr( $input ) { - return( strip_tags( $input ) ); +function validStr($input) { + return strip_tags($input); } // For strings in javascript or tags etc, expected to be in quotes so further quotes escaped rather than converted -function validJsStr( $input ) { - return( strip_tags( addslashes( $input ) ) ); +function validJsStr($input) { + return strip_tags(addslashes($input)); } // For general text in pages outside of tags or quotes so quotes converted to entities -function validHtmlStr( $input ) { - return( htmlspecialchars( $input, ENT_QUOTES ) ); +function validHtmlStr($input) { + return htmlspecialchars($input, ENT_QUOTES); } -function getStreamHTML( $monitor, $options = array() ) { +function getStreamHTML($monitor, $options = array()) { - if ( isset($options['scale']) and $options['scale'] and ( $options['scale'] != 100 ) ) { - //Warning("Scale to " . $options['scale'] ); - $options['width'] = reScale( $monitor->Width(), $options['scale'] ) . 'px'; - $options['height'] = reScale( $monitor->Height(), $options['scale'] ) . 'px'; + if ( isset($options['scale']) and $options['scale'] and ($options['scale'] != 100) ) { + $options['width'] = reScale($monitor->ViewWidth(), $options['scale']).'px'; + $options['height'] = reScale($monitor->ViewHeight(), $options['scale']).'px'; } else { # scale is empty or 100 # There may be a fixed width applied though, in which case we need to leave the height empty if ( ! ( isset($options['width']) and $options['width'] ) ) { - $options['width'] = $monitor->Width() . 'px'; + $options['width'] = $monitor->ViewWidth().'px'; if ( ! ( isset($options['height']) and $options['height'] ) ) { - $options['height'] = $monitor->Height() . 'px'; + $options['height'] = $monitor->ViewHeight().'px'; } } else if ( ! isset($options['height']) ) { $options['height'] = ''; @@ -2169,10 +2315,10 @@ function getStreamHTML( $monitor, $options = array() ) { //FIXME, the width and height of the image need to be scaled. } else if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) { $streamSrc = $monitor->getStreamSrc( array( - 'mode'=>'mpeg', - 'scale'=>(isset($options['scale'])?$options['scale']:100), - 'bitrate'=>ZM_WEB_VIDEO_BITRATE, - 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, + 'mode' => 'mpeg', + 'scale' => (isset($options['scale'])?$options['scale']:100), + 'bitrate'=> ZM_WEB_VIDEO_BITRATE, + 'maxfps' => ZM_WEB_VIDEO_MAXFPS, 'format' => ZM_MPEG_LIVE_FORMAT ) ); return getVideoStreamHTML( 'liveStream'.$monitor->Id(), $streamSrc, $options['width'], $options['height'], ZM_MPEG_LIVE_FORMAT, $monitor->Name() ); @@ -2185,53 +2331,79 @@ function getStreamHTML( $monitor, $options = array() ) { elseif ( canStreamApplet() ) // Helper, empty widths and heights really don't work. return getHelperStream( 'liveStream'.$monitor->Id(), $streamSrc, - $options['width'] ? $options['width'] : $monitor->Width(), - $options['height'] ? $options['height'] : $monitor->Height(), + $options['width'] ? $options['width'] : $monitor->ViewWidth(), + $options['height'] ? $options['height'] : $monitor->ViewHeight(), $monitor->Name()); } else { if ( $options['mode'] == 'stream' ) { - Info( 'The system has fallen back to single jpeg mode for streaming. Consider enabling Cambozola or upgrading the client browser.' ); + ZM\Info('The system has fallen back to single jpeg mode for streaming. Consider enabling Cambozola or upgrading the client browser.'); } $options['mode'] = 'single'; - $streamSrc = $monitor->getStreamSrc( $options ); - return getImageStill( 'liveStream'.$monitor->Id(), $streamSrc, $options['width'], $options['height'], $monitor->Name()); + $streamSrc = $monitor->getStreamSrc($options); + return getImageStill('liveStream'.$monitor->Id(), $streamSrc, $options['width'], $options['height'], $monitor->Name()); } } // end function getStreamHTML function getStreamMode( ) { $streamMode = ''; - if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) { + if ( (ZM_WEB_STREAM_METHOD == 'mpeg') && ZM_MPEG_LIVE_FORMAT ) { $streamMode = 'mpeg'; } elseif ( canStream() ) { $streamMode = 'jpeg'; } else { $streamMode = 'single'; - Info( 'The system has fallen back to single jpeg mode for streaming. Consider enabling Cambozola or upgrading the client browser.' ); + ZM\Info('The system has fallen back to single jpeg mode for streaming. Consider enabling Cambozola or upgrading the client browser.'); } return $streamMode; } // end function getStreamMode function folder_size($dir) { - $size = 0; - foreach (glob(rtrim($dir, '/').'/*', GLOB_NOSORT) as $each) { - $size += is_file($each) ? filesize($each) : folder_size($each); - } - return $size; + $size = 0; + foreach (glob(rtrim($dir, '/').'/*', GLOB_NOSORT) as $each) { + $size += is_file($each) ? filesize($each) : folder_size($each); + } + return $size; } // end function folder_size function human_filesize($size, $precision = 2) { - $units = array('B','kB','MB','GB','TB','PB','EB','ZB','YB'); - $step = 1024; - $i = 0; - while (($size / $step) > 0.9) { - $size = $size / $step; - $i++; - } - return round($size, $precision).$units[$i]; + $units = array('B','kB','MB','GB','TB','PB','EB','ZB','YB'); + $step = 1024; + $i = 0; + while (($size / $step) > 0.9) { + $size = $size / $step; + $i++; + } + return round($size, $precision).$units[$i]; } function csrf_startup() { - csrf_conf('rewrite-js', 'includes/csrf/csrf-magic.js'); + csrf_conf('rewrite-js', 'includes/csrf/csrf-magic.js'); +} + +function check_timezone() { + $now = new DateTime(); + + $sys_tzoffset = trim(shell_exec('date "+%z"')); + $php_tzoffset = trim($now->format('O')); + $mysql_tzoffset = trim(dbFetchOne( + 'SELECT TIME_FORMAT(TIMEDIFF(NOW(), UTC_TIMESTAMP),\'%H%i\');', + 'TIME_FORMAT(TIMEDIFF(NOW(), UTC_TIMESTAMP),\'%H%i\')' + )); + + #Logger::Debug("System timezone offset determine to be: $sys_tzoffset,\x20 + #PHP timezone offset determine to be: $php_tzoffset,\x20 + #Mysql timezone offset determine to be: $mysql_tzoffset + #"); + + if ( $sys_tzoffset != $php_tzoffset ) + ZM\Error("ZoneMinder is not configured properly: php's date.timezone $php_tzoffset does not match the system timezone $sys_tzoffset! Please check Options->System->Timezone."); + + if ( $sys_tzoffset != $mysql_tzoffset ) + ZM\Error("ZoneMinder is not configured properly: mysql's timezone does not match the system timezone! Event lists will display incorrect times."); + + if (!ini_get('date.timezone') || !date_default_timezone_set(ini_get('date.timezone'))) + ZM\Error("ZoneMinder is not configured properly: php's date.timezone is not set to a valid timezone. Please check Options->System->Timezone"); + } function unparse_url($parsed_url, $substitutions = array() ) { @@ -2253,4 +2425,194 @@ function unparse_url($parsed_url, $substitutions = array() ) { $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : ''; return "$scheme$user$pass$host$port$path$query$fragment"; } + +// PP - POST request handler for PHP which does not need extensions +// credit: http://wezfurlong.org/blog/2006/nov/http-post-from-php-without-curl/ + +function do_request($method, $url, $data=array(), $optional_headers = null) { + global $php_errormsg; + + $params = array('http' => array( + 'method' => $method, + 'content' => $data + )); + if ( $optional_headers !== null ) { + $params['http']['header'] = $optional_headers; + } + $ctx = stream_context_create($params); + $fp = @fopen($url, 'rb', false, $ctx); + if ( !$fp ) { + throw new Exception("Problem with $url, $php_errormsg"); + } + $response = @stream_get_contents($fp); + if ( $response === false ) { + throw new Exception("Problem reading data from $url, $php_errormsg"); + } + return $response; +} + +function do_post_request($url, $data, $optional_headers = null) { + $params = array('http' => array( + 'method' => 'POST', + 'content' => $data + )); + if ( $optional_headers !== null ) { + $params['http']['header'] = $optional_headers; + } + $ctx = stream_context_create($params); + $fp = @fopen($url, 'rb', false, $ctx); + if ( !$fp ) { + throw new Exception("Problem with $url, " + .print_r(error_get_last(),true)); + } + $response = @stream_get_contents($fp); + if ( $response === false ) { + throw new Exception("Problem reading data from $url, data: ".print_r($params,true) + .print_r(error_get_last(),true)); + } + return $response; +} + +// The following works around php not being built with semaphore functions. +if ( !function_exists('sem_get') ) { + function sem_get($key) { + return fopen(__FILE__ . '.sem.' . $key, 'w+'); + } + function sem_acquire($sem_id) { + return flock($sem_id, LOCK_EX); + } + function sem_release($sem_id) { + return flock($sem_id, LOCK_UN); + } +} + +if ( !function_exists('ftok') ) { + function ftok($filename = "", $proj = "") { + if ( empty($filename) || !file_exists($filename) ) { + return -1; + } else { + $filename = $filename . (string) $proj; + for($key = array(); sizeof($key) < strlen($filename); $key[] = ord(substr($filename, sizeof($key), 1))); + return dechex(array_sum($key)); + } + } +} + +function getAffectedIds( $name ) { + $names = $name.'s'; + $ids = array(); + if ( isset($_REQUEST[$names]) ) { + if ( is_array($_REQUEST[$names]) ) { + $ids = $_REQUEST[$names]; + } else { + $ids = array($_REQUEST[$names]); + } + } else if ( isset($_REQUEST[$name]) ) { + if ( is_array($_REQUEST[$name]) ) { + $ids = $_REQUEST[$name]; + } else { + $ids = array($_REQUEST[$name]); + } + } + return $ids; +} + +function format_duration($time, $separator=':') { + return sprintf('%02d%s%02d%s%02d', floor($time/3600), $separator, ($time/60)%60, $separator, $time%60); +} + +function array_recursive_diff($aArray1, $aArray2) { + $aReturn = array(); + if ( ! (is_array($aArray1) and is_array($aArray2) ) ) { + $backTrace = debug_backtrace(); + ZM\Warning("Bad arrays passed 1:" . print_r($aArray1,true) . "\n2: " . print_r($aArray2,true)."\n from: ".print_r($backTrace,true)); + return; + + } + + foreach ( $aArray1 as $mKey => $mValue ) { + if ( array_key_exists($mKey, $aArray2) ) { + if ( is_array($mValue) ) { + if ( is_array($aArray2[$mKey]) ) { + $aRecursiveDiff = array_recursive_diff($mValue, $aArray2[$mKey]); + if ( count($aRecursiveDiff) ) { + $aReturn[$mKey] = $aRecursiveDiff; + } + } else { + $aReturn[$mKey] = $mValue; + } + } else { + if ( $mValue != $aArray2[$mKey] ) { + $aReturn[$mKey] = $mValue; + } + } + } else { + $aReturn[$mKey] = $mValue; + } + } + # Now check for keys in array2 that are not in array1 + foreach ($aArray2 as $mKey => $mValue) { + if ( array_key_exists($mKey, $aArray1) ) { + # Already checked it... I think. + #if ( is_array($mValue) ) { + #$aRecursiveDiff = array_recursive_diff($mValue, $aArray2[$mKey]); + #if ( count($aRecursiveDiff) ) { + #$aReturn[$mKey] = $aRecursiveDiff; + #} + #} else { + #if ( $mValue != $aArray2[$mKey] ) { + #$aReturn[$mKey] = $mValue; + #} + #} + } else { + $aReturn[$mKey] = $mValue; + } + } + + return $aReturn; +} + +function html_radio($name, $values, $selected=null, $options=array(), $attrs=array()) { + + $html = ''; + if ( isset($options['default']) and ( $selected == null ) ) { + $selected = $options['default']; + } # end if + + foreach ( $values as $value => $label ) { + if ( isset($options['container']) ) { + $html .= $options['container'][0]; + } + $attributes = array_map( + function($attr, $value){return $attr.'="'.$value.'"';}, + array_keys($attrs), + array_values($attrs) + ); + $attributes_string = implode(' ', $attributes); + + $html .= sprintf(' +
+
+ ', $name, $value, $label, ($value==$selected?' checked="checked"':''), + $attributes_string, + (isset($options['id']) ? $options['id'] : ''), + ( ( (!isset($options['inline'])) or $options['inline'] ) ? '-inline' : '') + ); + if ( isset($options['container']) ) { + $html .= $options['container'][1]; + } + } # end foreach value + return $html; +} # end sub html_radio + + +function random_colour() { + return '#'. + str_pad( dechex( mt_rand( 0, 255 ) ), 2, '0', STR_PAD_LEFT). + str_pad( dechex( mt_rand( 0, 255 ) ), 2, '0', STR_PAD_LEFT). + str_pad( dechex( mt_rand( 0, 255 ) ), 2, '0', STR_PAD_LEFT); +} + ?> diff --git a/web/includes/lang.php b/web/includes/lang.php index 0cab566b7..38f1179d8 100644 --- a/web/includes/lang.php +++ b/web/includes/lang.php @@ -1,6 +1,6 @@ $value) { + if ( ! array_key_exists($key, $SLANG) ) + $SLANG[$key] = $DLANG[$key]; + } +} // // Date and time formats fallback, if not set up by the language file already // -defined("DATE_FMT_CONSOLE_LONG") or define("DATE_FMT_CONSOLE_LONG", "D jS M, g:ia"); // This is the main console date/time, date() or strftime() format -defined("DATE_FMT_CONSOLE_SHORT") or define( "DATE_FMT_CONSOLE_SHORT", "%H:%M" ); // This is the xHTML console date/time, date() or strftime() format +defined('DATE_FMT_CONSOLE_LONG') or define('DATE_FMT_CONSOLE_LONG', 'D jS M, g:ia'); // This is the main console date/time, date() or strftime() format +defined('DATE_FMT_CONSOLE_SHORT') or define('DATE_FMT_CONSOLE_SHORT', '%H:%M'); // This is the xHTML console date/time, date() or strftime() format -defined("STRF_FMT_DATETIME") or define( "STRF_FMT_DATETIME", "%c" ); // Strftime locale aware format for dates with times -defined("STRF_FMT_DATE") or define( "STRF_FMT_DATE", "%x" ); // Strftime locale aware format for dates without times -defined("STRF_FMT_TIME") or define( "STRF_FMT_TIME", "%X" ); // Strftime locale aware format for times without dates +defined('STRF_FMT_DATETIME') or define('STRF_FMT_DATETIME', '%c'); // Strftime locale aware format for dates with times +defined('STRF_FMT_DATE') or define('STRF_FMT_DATE', '%x'); // Strftime locale aware format for dates without times +defined('STRF_FMT_TIME') or define('STRF_FMT_TIME', '%X'); // Strftime locale aware format for times without dates -defined("STRF_FMT_DATETIME_SHORT") or define( "STRF_FMT_DATETIME_SHORT", "%y/%m/%d %H:%M:%S" ); // Strftime shorter format for dates with time, not locale aware -defined("STRF_FMT_DATETIME_SHORTER") or define( "STRF_FMT_DATETIME_SHORTER", "%m/%d %H:%M:%S" );// Strftime shorter format for dates with time, not locale aware, used where space is tight +defined('STRF_FMT_DATETIME_SHORT') or define('STRF_FMT_DATETIME_SHORT', '%y/%m/%d %H:%M:%S'); // Strftime shorter format for dates with time, not locale aware +defined('STRF_FMT_DATETIME_SHORTER') or define('STRF_FMT_DATETIME_SHORTER','%m/%d %H:%M:%S');// Strftime shorter format for dates with time, not locale aware, used where space is tight ?> diff --git a/web/includes/logger.php b/web/includes/logger.php index 0d7334ac7..2b5463dfb 100644 --- a/web/includes/logger.php +++ b/web/includes/logger.php @@ -1,5 +1,6 @@ "DBG", - self::INFO => "INF", - self::WARNING => "WAR", - self::ERROR => "ERR", - self::FATAL => "FAT", - self::PANIC => "PNC", - self::NOLOG => "OFF", + self::DEBUG => 'DBG', + self::INFO => 'INF', + self::WARNING => 'WAR', + self::ERROR => 'ERR', + self::FATAL => 'FAT', + self::PANIC => 'PNC', + self::NOLOG => 'OFF', ); private static $syslogPriorities = array( self::DEBUG => LOG_DEBUG, @@ -62,7 +63,7 @@ class Logger { private function __construct() { $this->hasTerm = (php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR'])); - $this->logFile = $this->logPath."/".$this->id.".log"; + $this->logFile = $this->logPath.'/'.$this->id.'.log'; } public function __destruct() { @@ -77,12 +78,12 @@ class Logger { //$this->useErrorLog = $options['useErrorLog']; if ( isset($options['logPath']) ) { $this->logPath = $options['logPath']; - $tempLogFile = $this->logPath."/".$this->id.".log"; + $tempLogFile = $this->logPath.'/'.$this->id.'.log'; } if ( isset($options['logFile']) ) $tempLogFile = $options['logFile']; else - $tempLogFile = $this->logPath."/".$this->id.".log"; + $tempLogFile = $this->logPath.'/'.$this->id.'.log'; if ( !is_null($logFile = $this->getTargettedEnv('LOG_FILE')) ) $tempLogFile = $logFile; @@ -176,10 +177,10 @@ class Logger { } private function getTargettedEnv( $name ) { - $envName = $name."_".$this->id; + $envName = $name.'_'.$this->id; $value = getenv( $envName ); if ( $value === false && $this->id != $this->idRoot ) - $value = getenv( $name."_".$this->idRoot ); + $value = getenv( $name.'_'.$this->idRoot ); if ( $value === false ) $value = getenv( $name ); return( $value !== false ? $value : NULL ); @@ -268,12 +269,12 @@ class Logger { if ( $this->databaseLevel > self::NOLOG ) { if ( (include_once 'database.php') === FALSE ) { $this->databaseLevel = self::NOLOG; - Warning( "Unable to write log entries to DB, database.php not found" ); + Warning( 'Unable to write log entries to DB, database.php not found' ); } } } } - return( $this->databaseLevel ); + return $this->databaseLevel; } public function fileLevel( $fileLevel ) { @@ -287,7 +288,7 @@ class Logger { $this->openFile(); } } - return( $this->fileLevel ); + return $this->fileLevel; } public function weblogLevel( $weblogLevel ) { @@ -302,7 +303,7 @@ class Logger { $this->weblogLevel = $weblogLevel; } } - return( $this->weblogLevel ); + return $this->weblogLevel; } public function syslogLevel( $syslogLevel ) { @@ -316,30 +317,31 @@ class Logger { $this->openSyslog(); } } - return( $this->syslogLevel ); + return $this->syslogLevel; } private function openSyslog() { - openlog( $this->id, LOG_PID|LOG_NDELAY, LOG_LOCAL1 ); + openlog($this->id, LOG_PID|LOG_NDELAY, LOG_LOCAL1); } private function closeSyslog() { closelog(); } - private function logFile( $logFile ) { - if ( preg_match( '/^(.+)\+$/', $logFile, $matches ) ) + private function logFile($logFile) { + if ( preg_match('/^(.+)\+$/', $logFile, $matches) ) { $this->logFile = $matches[1].'.'.getmypid(); - else + } else { $this->logFile = $logFile; + } } private function openFile() { if ( !$this->useErrorLog ) { - if ( $this->logFd = fopen( $this->logFile, 'a+' ) ) { - if ( strnatcmp( phpversion(), '5.2.0' ) >= 0 ) { + if ( $this->logFd = fopen($this->logFile, 'a+') ) { + if ( strnatcmp(phpversion(), '5.2.0') >= 0 ) { $error = error_get_last(); - trigger_error( "Can't open log file '$logFile': ".$error['message'].' @ '.$error['file'].'/'.$error['line'], E_USER_ERROR ); + trigger_error("Can't open log file '$logFile': ".$error['message'].' @ '.$error['file'].'/'.$error['line'], E_USER_ERROR); } $this->fileLevel = self::NOLOG; } @@ -348,73 +350,83 @@ class Logger { private function closeFile() { if ( $this->logFd ) - fclose( $this->logFd ); + fclose($this->logFd); } public function logPrint( $level, $string, $file=NULL, $line=NULL ) { - if ( $level <= $this->effectiveLevel ) { - $string = preg_replace( '/[\r\n]+$/', '', $string ); - $code = self::$codes[$level]; + if ( $level > $this->effectiveLevel ) { + return; + } - $time = gettimeofday(); - $message = sprintf( '%s.%06d %s[%d].%s [%s]', strftime( '%x %H:%M:%S', $time['sec'] ), $time['usec'], $this->id, getmypid(), $code, $string ); + $string = preg_replace('/[\r\n]+$/', '', $string); + $code = self::$codes[$level]; - if ( is_null($file) ) { - if ( $this->useErrorLog || $this->databaseLevel > self::NOLOG ) { - $backTrace = debug_backtrace(); - $file = $backTrace[1]['file']; - $line = $backTrace[1]['line']; - if ( $this->hasTerm ) - $rootPath = getcwd(); - else - $rootPath = $_SERVER['DOCUMENT_ROOT']; - $file = preg_replace( '/^'.addcslashes($rootPath,'/').'\/?/', '', $file ); - } - } + $time = gettimeofday(); + $message = sprintf('%s.%06d %s[%d].%s [%s] [%s]', + strftime('%x %H:%M:%S', $time['sec']), $time['usec'], + $this->id, getmypid(), $code, $_SERVER['REMOTE_ADDR'], $string); - if ( $this->useErrorLog ) - $message .= ' at '.$file.' line '.$line; - else - $message = $message; - - if ( $level <= $this->termLevel ) + if ( is_null($file) ) { + if ( $this->useErrorLog || ($this->databaseLevel > self::NOLOG) ) { + $backTrace = debug_backtrace(); + $file = $backTrace[1]['file']; + $line = $backTrace[1]['line']; if ( $this->hasTerm ) - print( $message."\n" ); + $rootPath = getcwd(); else - print( preg_replace( "/\n/", '
', htmlspecialchars($message) ).'
' ); - - if ( $level <= $this->fileLevel ) - if ( $this->useErrorLog ) { - if ( !error_log( $message."\n", 3, $this->logFile ) ) { - if ( strnatcmp( phpversion(), '5.2.0' ) >= 0 ) { - $error = error_get_last(); - trigger_error( "Can't write to log file '".$this->logFile."': ".$error['message'].' @ '.$error['file'].'/'.$error['line'], E_USER_ERROR ); - } - } - } elseif ( $this->logFd ) { - fprintf( $this->logFd, $message."\n" ); - } - - $message = $code.' ['.$string.']'; - if ( $level <= $this->syslogLevel ) - syslog( self::$syslogPriorities[$level], $message ); - if ( $level <= $this->databaseLevel ) { - try { - global $dbConn; - $sql = 'INSERT INTO Logs ( TimeKey, Component, Pid, Level, Code, Message, File, Line ) values ( ?, ?, ?, ?, ?, ?, ?, ? )'; - $stmt = $dbConn->prepare( $sql ); - $result = $stmt->execute( array( sprintf( '%d.%06d', $time['sec'], $time['usec'] ), $this->id, getmypid(), $level, $code, $string, $file, $line ) ); - } catch(PDOException $ex) { - $this->databaseLevel = self::NOLOG; - Fatal( "Can't write log entry '$sql': ". $ex->getMessage() ); - } + $rootPath = $_SERVER['DOCUMENT_ROOT']; + $file = preg_replace('/^'.addcslashes($rootPath,'/').'\/?/', '', $file); } - // This has to be last as trigger_error can be fatal - if ( $level <= $this->weblogLevel ) { - if ( $this->useErrorLog ) - error_log( $message, 0 ); - else - trigger_error( $message, self::$phpErrorLevels[$level] ); + } + + if ( $this->useErrorLog ) { + $message .= ' at '.$file.' line '.$line; + } else { + $message = $message; + } + + if ( $level <= $this->termLevel ) { + if ( $this->hasTerm ) + print($message."\n"); + else + print(preg_replace("/\n/", '
', htmlspecialchars($message)).'
'); + } + + if ( $level <= $this->fileLevel ) { + if ( $this->useErrorLog ) { + if ( !error_log($message."\n", 3, $this->logFile) ) { + if ( strnatcmp(phpversion(), '5.2.0') >= 0 ) { + $error = error_get_last(); + trigger_error("Can't write to log file '".$this->logFile."': ".$error['message'].' @ '.$error['file'].'/'.$error['line'], E_USER_ERROR); + } + } + } else if ( $this->logFd ) { + fprintf($this->logFd, $message."\n"); + } + } + + $message = $code.' ['.$string.']'; + if ( $level <= $this->syslogLevel ) + syslog( self::$syslogPriorities[$level], $message ); + + if ( $level <= $this->databaseLevel ) { + try { + global $dbConn; + $sql = 'INSERT INTO `Logs` ( `TimeKey`, `Component`, `ServerId`, `Pid`, `Level`, `Code`, `Message`, `File`, `Line` ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ? )'; + $stmt = $dbConn->prepare($sql); + $result = $stmt->execute(array(sprintf('%d.%06d', $time['sec'], $time['usec']), $this->id, + (defined('ZM_SERVER_ID') ? ZM_SERVER_ID : null), getmypid(), $level, $code, $string, $file, $line)); + } catch(PDOException $ex) { + $this->databaseLevel = self::NOLOG; + Error("Can't write log entry '$sql': ". $ex->getMessage()); + } + } + // This has to be last as trigger_error can be fatal + if ( $level <= $this->weblogLevel ) { + if ( $this->useErrorLog ) { + error_log($message, 0); + } else { + trigger_error($message, self::$phpErrorLevels[$level]); } } } @@ -423,7 +435,7 @@ class Logger { function logInit( $options=array() ) { $logger = Logger::fetch(); $logger->initialise( $options ); - set_error_handler( 'ErrorHandler' ); + set_error_handler( 'ZM\ErrorHandler' ); } function logToDatabase( $level=NULL ) { @@ -455,7 +467,10 @@ function Error( $string ) { function Fatal( $string ) { Logger::fetch()->logPrint( Logger::FATAL, $string ); - die( htmlentities($string) ); + if (Logger::fetch()->debugOn()) { + echo(htmlentities($string)); + } + exit(1); } function Panic( $string ) { @@ -474,13 +489,16 @@ function Panic( $string ) { } } Logger::fetch()->logPrint( Logger::PANIC, $string.$backtrace ); - die( $string ); + if (Logger::fetch()->debugOn()) { + echo $string; + } + exit(1); } function ErrorHandler( $error, $string, $file, $line ) { if ( ! (error_reporting() & $error) ) { // This error code is not included in error_reporting - return( false ); + return false; } switch ( $error ) { @@ -500,7 +518,7 @@ function ErrorHandler( $error, $string, $file, $line ) { Panic( "Unknown error type: [$error] $string" ); break; } - return( true ); + return true; } ?> diff --git a/web/includes/session.php b/web/includes/session.php new file mode 100644 index 000000000..dec414fec --- /dev/null +++ b/web/includes/session.php @@ -0,0 +1,82 @@ +=') ) { + return session_status() === PHP_SESSION_ACTIVE ? TRUE : FALSE; + } else { + return session_id() === '' ? FALSE : TRUE; + } + } else { + Warning("php_sapi_name === 'cli'"); + } + return FALSE; +} // function is_session_started() + +function zm_session_clear() { + session_start(); + $_SESSION = array(); + if ( ini_get('session.use_cookies') ) { + $p = session_get_cookie_params(); + # Update the cookie to expire in the past. + setcookie(session_name(), '', time() - 31536000, $p['path'], $p['domain'], $p['secure'], $p['httponly']); + } + session_unset(); + session_destroy(); + session_write_close(); + session_start(); +} // function zm_session_clear() +?> diff --git a/web/index.php b/web/index.php index 4cc7cf75c..2f1a6cf62 100644 --- a/web/index.php +++ b/web/index.php @@ -18,17 +18,17 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -error_reporting( E_ALL ); +error_reporting(E_ALL); $debug = false; if ( $debug ) { // Use these for debugging, though not both at once! - phpinfo( INFO_VARIABLES ); + phpinfo(INFO_VARIABLES); //error_reporting( E_ALL ); } // Use new style autoglobals where possible -if ( version_compare( phpversion(), '4.1.0', '<') ) { +if ( version_compare(phpversion(), '4.1.0', '<') ) { $_SESSION = &$HTTP_SESSION_VARS; $_SERVER = &$HTTP_SERVER_VARS; } @@ -36,38 +36,45 @@ if ( version_compare( phpversion(), '4.1.0', '<') ) { // Useful debugging lines for mobile devices if ( false ) { ob_start(); - phpinfo( INFO_VARIABLES ); - $fp = fopen( '/tmp/env.html', 'w' ); - fwrite( $fp, ob_get_contents() ); - fclose( $fp ); + phpinfo(INFO_VARIABLES); + $fp = fopen('/tmp/env.html', 'w+'); + fwrite($fp, ob_get_contents()); + fclose($fp); ob_end_clean(); } -require_once( 'includes/config.php' ); -require_once( 'includes/logger.php' ); -require_once( 'includes/Server.php' ); -require_once( 'includes/Storage.php' ); -require_once( 'includes/Event.php' ); -require_once( 'includes/Group.php' ); -require_once( 'includes/Monitor.php' ); +require_once('includes/config.php'); +require_once('includes/session.php'); +require_once('includes/logger.php'); +require_once('includes/Server.php'); +require_once('includes/Storage.php'); +require_once('includes/Event.php'); +require_once('includes/Group.php'); +require_once('includes/Monitor.php'); -if ( isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ) { +if ( + (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') + or + (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) and ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) +) { $protocol = 'https'; } else { $protocol = 'http'; } -define( 'ZM_BASE_PROTOCOL', $protocol ); +define('ZM_BASE_PROTOCOL', $protocol); // Absolute URL's are unnecessary and break compatibility with reverse proxies // define( "ZM_BASE_URL", $protocol.'://'.$_SERVER['HTTP_HOST'] ); // Use relative URL's instead -define( 'ZM_BASE_URL', '' ); +define('ZM_BASE_URL', ''); -// Check time zone is set -if (!ini_get('date.timezone') || !date_default_timezone_set(ini_get('date.timezone'))) { - date_default_timezone_set('UTC'); - Fatal( "ZoneMinder is not installed properly: php's date.timezone is not set to a valid timezone" ); +require_once('includes/functions.php'); +if ( $_SERVER['REQUEST_METHOD'] == 'OPTIONS' ) { + ZM\Logger::Debug("OPTIONS Method, only doing CORS"); + # Add Cross domain access headers + CORSHeaders(); + return; } if ( isset($_GET['skin']) ) { @@ -80,78 +87,73 @@ if ( isset($_GET['skin']) ) { $skin = 'classic'; } -$skins = array_map( 'basename', glob('skins/*', GLOB_ONLYDIR ) ); +if ( ! is_dir("skins/$skin") ) { + $skins = array_map('basename', glob('skins/*', GLOB_ONLYDIR)); -if ( ! in_array( $skin, $skins ) ) { - Error( "Invalid skin '$skin' setting to " . $skins[0] ); - $skin = $skins[0]; + if ( !in_array($skin, $skins) ) { + ZM\Error("Invalid skin '$skin' setting to ".$skins[0]); + $skin = $skins[0]; + } } if ( isset($_GET['css']) ) { $css = $_GET['css']; -} elseif ( isset($_COOKIE['zmCSS']) ) { +} else if ( isset($_COOKIE['zmCSS']) ) { $css = $_COOKIE['zmCSS']; -} elseif (defined('ZM_CSS_DEFAULT')) { +} else if ( defined('ZM_CSS_DEFAULT') ) { $css = ZM_CSS_DEFAULT; } else { $css = 'classic'; } -$css_skins = array_map( 'basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR) ); -if ( ! in_array( $css, $css_skins ) ) { - Error( "Invalid skin css '$css' setting to " . $css_skins[0] ); - $css = $css_skins[0]; +if ( !is_dir("skins/$skin/css/$css") ) { + $css_skins = array_map('basename', glob('skins/'.$skin.'/css/*', GLOB_ONLYDIR)); + if ( count($css_skins) ) { + if ( !in_array($css, $css_skins) ) { + ZM\Error("Invalid skin css '$css' setting to " . $css_skins[0]); + $css = $css_skins[0]; + } else { + $css = ''; + } + } else { + ZM\Error("No css options found at skins/$skin/css"); + $css = ''; + } } -define( 'ZM_BASE_PATH', dirname( $_SERVER['REQUEST_URI'] ) ); -define( 'ZM_SKIN_PATH', "skins/$skin" ); -define( 'ZM_SKIN_NAME', $skin ); +define('ZM_BASE_PATH', dirname($_SERVER['REQUEST_URI'])); +define('ZM_SKIN_PATH', "skins/$skin"); +define('ZM_SKIN_NAME', $skin); $skinBase = array(); // To allow for inheritance of skins -if ( !file_exists( ZM_SKIN_PATH ) ) - Fatal( "Invalid skin '$skin'" ); +if ( !file_exists(ZM_SKIN_PATH) ) + ZM\Fatal("Invalid skin '$skin'"); $skinBase[] = $skin; -$currentCookieParams = session_get_cookie_params(); -//Logger::Debug('Setting cookie parameters to lifetime('.$currentCookieParams['lifetime'].') path('.$currentCookieParams['path'].') domain ('.$currentCookieParams['domain'].') secure('.$currentCookieParams['secure'].') httpOnly(1)'); -session_set_cookie_params( - $currentCookieParams['lifetime'], - $currentCookieParams['path'], - $currentCookieParams['domain'], - $currentCookieParams['secure'], - true -); +zm_session_start(); -ini_set( 'session.name', 'ZMSESSID' ); - -session_start(); - -if ( !isset($_SESSION['skin']) || isset($_REQUEST['skin']) || !isset($_COOKIE['zmSkin']) || $_COOKIE['zmSkin'] != $skin ) { +if ( + !isset($_SESSION['skin']) || + isset($_REQUEST['skin']) || + !isset($_COOKIE['zmSkin']) || + ($_COOKIE['zmSkin'] != $skin) +) { $_SESSION['skin'] = $skin; - setcookie( 'zmSkin', $skin, time()+3600*24*30*12*10 ); + setcookie('zmSkin', $skin, time()+3600*24*30*12*10); } -if ( !isset($_SESSION['css']) || isset($_REQUEST['css']) || !isset($_COOKIE['zmCSS']) || $_COOKIE['zmCSS'] != $css ) { +if ( + !isset($_SESSION['css']) || + isset($_REQUEST['css']) || + !isset($_COOKIE['zmCSS']) || + ($_COOKIE['zmCSS'] != $css) +) { $_SESSION['css'] = $css; - setcookie( 'zmCSS', $css, time()+3600*24*30*12*10 ); + setcookie('zmCSS', $css, time()+3600*24*30*12*10); } -if ( ZM_OPT_USE_AUTH ) { - if ( isset( $_SESSION['user'] ) ) { - $user = $_SESSION['user']; - } else { - unset( $user ); - } -} else { - $user = $defaultUser; -} -# Only one request can open the session file at a time, so let's close the session here to improve concurrency. -# Any file/page that sets session variables must re-open it. -session_write_close(); -require_once( 'includes/lang.php' ); -require_once( 'includes/functions.php' ); -require_once( 'includes/auth.php' ); +require_once('includes/lang.php'); # Running is global but only do the daemonCheck if it is actually needed $running = null; @@ -160,96 +162,135 @@ $running = null; CORSHeaders(); // Check for valid content dirs -if ( !is_writable(ZM_DIR_EVENTS) || !is_writable(ZM_DIR_IMAGES) ) { - Warning("Cannot write to content dirs('".ZM_DIR_EVENTS."','".ZM_DIR_IMAGES."'). Check that these exist and are owned by the web account user"); +if ( !is_writable(ZM_DIR_EVENTS) ) { + ZM\Warning("Cannot write to event folder ".ZM_DIR_EVENTS.". Check that it exists and is owned by the web account user."); } # Globals +$action = null; +$error_message = null; $redirect = null; $view = null; +$user = null; if ( isset($_REQUEST['view']) ) $view = detaintPath($_REQUEST['view']); + +# Add CSP Headers +$cspNonce = bin2hex(openssl_random_pseudo_bytes(16)); + $request = null; if ( isset($_REQUEST['request']) ) $request = detaintPath($_REQUEST['request']); -foreach ( getSkinIncludes( 'skin.php' ) as $includeFile ) +require_once('includes/auth.php'); +# Only one request can open the session file at a time, so let's close the session here to improve concurrency. +# Any file/page that sets session variables must re-open it. +session_write_close(); + +foreach ( getSkinIncludes('skin.php') as $includeFile ) { require_once $includeFile; - -if ( ZM_OPT_USE_AUTH ) { - if ( ZM_AUTH_HASH_LOGINS ) { - if ( empty($user) && ! empty($_REQUEST['auth']) ) { - if ( $authUser = getAuthUser( $_REQUEST['auth'] ) ) { - userLogin( $authUser['Username'], $authUser['Password'], true ); - } - } - } - if ( ! empty($user) ) { - // generate it once here, while session is open. Value will be cached in session and return when called later on - generateAuthHash( ZM_AUTH_HASH_IPS ); - } } -if ( isset($_REQUEST['action']) ) { +if ( isset($_REQUEST['action']) ) $action = detaintPath($_REQUEST['action']); -} # The only variable we really need to set is action. The others are informal. isset($view) || $view = NULL; isset($request) || $request = NULL; isset($action) || $action = NULL; -if ( ZM_ENABLE_CSRF_MAGIC && $action != 'login' && $view != 'view_video' && $request != 'control' && $view != 'frames' && $view != 'archive' ) { - require_once( 'includes/csrf/csrf-magic.php' ); - #Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\""); +if ( (!$view and !$request) or ($view == 'console') ) { + // Verify the system, php, and mysql timezones all match + #if ( ZM_TIMEZONE ) + #date_default_timezone_set(ZM_TIMEZONE); + check_timezone(); +} + +ZM\Logger::Debug("View: $view Request: $request Action: $action User: " . ( isset($user) ? $user['Username'] : 'none' )); +if ( + ZM_ENABLE_CSRF_MAGIC && + ( $action != 'login' ) && + ( $view != 'view_video' ) && + ( $view != 'image' ) && + ( $request != 'control' ) && + ( $view != 'frames' ) && + ( $view != 'archive' ) +) { + require_once('includes/csrf/csrf-magic.php'); + #ZM\Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\""); csrf_check(); } # Need to include actions because it does auth -require_once( 'includes/actions.php' ); +if ( $action and !$request ) { + if ( file_exists('includes/actions/'.$view.'.php') ) { + ZM\Logger::Debug("Including includes/actions/$view.php"); + require_once('includes/actions/'.$view.'.php'); + } else { + ZM\Warning("No includes/actions/$view.php for action $action"); + } +} # If I put this here, it protects all views and popups, but it has to go after actions.php because actions.php does the actual logging in. -if ( ZM_OPT_USE_AUTH and ! isset($user) ) { - Logger::Debug("Redirecting to login" ); - $view = 'login'; +if ( ZM_OPT_USE_AUTH and (!isset($user)) and ($view != 'login') and ($view != 'none') ) { + /* AJAX check */ + if ( !empty($_SERVER['HTTP_X_REQUESTED_WITH']) + && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest' ) { + header('HTTP/1.1 401 Unauthorized'); + exit; + } + ZM\Logger::Debug('Redirecting to login'); + $view = 'none'; + $redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=login'; + if ( ! $request ) { + zm_session_start(); + $_SESSION['postLoginQuery'] = $_SERVER['QUERY_STRING']; + session_write_close(); + } + $request = null; +} else if ( ZM_SHOW_PRIVACY && ($view != 'privacy') && ($view != 'options') && (!$request) && canEdit('System') ) { + $view = 'none'; + $redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=privacy'; $request = null; } +CSPHeaders($view, $cspNonce); if ( $redirect ) { + ZM\Logger::Debug("Redirecting to $redirect"); header('Location: '.$redirect); return; } if ( $request ) { - foreach ( getSkinIncludes( 'ajax/'.$request.'.php', true, true ) as $includeFile ) { - if ( !file_exists( $includeFile ) ) - Fatal( "Request '$request' does not exist" ); + foreach ( getSkinIncludes('ajax/'.$request.'.php', true, true) as $includeFile ) { + if ( !file_exists($includeFile) ) + ZM\Fatal("Request '$request' does not exist"); require_once $includeFile; } return; -} else { - if ( $includeFiles = getSkinIncludes( 'views/'.$view.'.php', true, true ) ) { - foreach ( $includeFiles as $includeFile ) { - if ( !file_exists( $includeFile ) ) - Fatal( "View '$view' does not exist" ); - require_once $includeFile; - } - // If the view overrides $view to 'error', and the user is not logged in, then the - // issue is probably resolvable by logging in, so provide the opportunity to do so. - // The login view should handle redirecting to the correct location afterward. - if ( $view == 'error' && !isset($user) ) { - $view = 'login'; - foreach ( getSkinIncludes( 'views/login.php', true, true ) as $includeFile ) - require_once $includeFile; - } +} + +if ( $includeFiles = getSkinIncludes('views/'.$view.'.php', true, true) ) { + foreach ( $includeFiles as $includeFile ) { + if ( !file_exists($includeFile) ) + ZM\Fatal("View '$view' does not exist"); + require_once $includeFile; } - // If the view is missing or the view still returned error with the user logged in, - // then it is not recoverable. - if ( !$includeFiles || $view == 'error' ) { - foreach ( getSkinIncludes( 'views/error.php', true, true ) as $includeFile ) + // If the view overrides $view to 'error', and the user is not logged in, then the + // issue is probably resolvable by logging in, so provide the opportunity to do so. + // The login view should handle redirecting to the correct location afterward. + if ( $view == 'error' && !isset($user) ) { + $view = 'login'; + foreach ( getSkinIncludes('views/login.php', true, true) as $includeFile ) require_once $includeFile; } } +// If the view is missing or the view still returned error with the user logged in, +// then it is not recoverable. +if ( !$includeFiles || $view == 'error' ) { + foreach ( getSkinIncludes('views/error.php', true, true) as $includeFile ) + require_once $includeFile; +} ?> diff --git a/web/js/Server.js b/web/js/Server.js new file mode 100644 index 000000000..d9ab937e6 --- /dev/null +++ b/web/js/Server.js @@ -0,0 +1,40 @@ +'use strict'; + +var _createClass = function() { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); + } + } return function(Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; + }; +}(); + +function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +} + +var Server = function() { + function Server(json) { + _classCallCheck(this, Server); + + for (var k in json) { + this[k] = json[k]; + } + } + + _createClass(Server, [{ + key: 'url', + value: function url() { + var port = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + + return location.protocol + '//' + this.Hostname + (port ? ':' + port : '') + (this.PathPrefix && this.PathPrefix != 'null' ? this.PathPrefix : ''); + } + }]); + + return Server; +}(); + +; diff --git a/web/js/logger.js b/web/js/logger.js index 6bd465c8a..0a1195043 100644 --- a/web/js/logger.js +++ b/web/js/logger.js @@ -17,105 +17,112 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -if ( !window.console ) -{ - window.console = +if ( !window.console ) { + window.console = { - init:function() {}, - log:function() {}, - debug:function() {}, - info:function() {}, - warn:function() {}, - error:function() {} + init: function() {}, + log: function() {}, + debug: function() {}, + info: function() {}, + warn: function() {}, + error: function() {} }; } -if ( !console.debug )//IE8 has console but doesn't have console.debug so lets alias it. - console.debug = console.log; +if ( !console.debug ) { + // IE8 has console but doesn't have console.debug so lets alias it. + console.debug = console.log; +} var reportLogs = true; var debugParms; var debugReq; -function logReport( level, message, file, line ) -{ - if ( !reportLogs ) - return; +function logReport( level, message, file, line ) { + if ( !reportLogs ) { + return; + } - if ( typeof(MooTools) == "undefined" ) - return; + if ( typeof(MooTools) == "undefined" ) { + return; + } + /* eslint-disable no-caller */ + if ( arguments && arguments.callee && arguments.callee.caller && arguments.callee.caller.caller && arguments.callee.caller.caller.name ) { + message += ' - '+arguments.callee.caller.caller.name+'()'; + } + /* eslint-enable no-caller */ - if ( arguments && arguments.callee && arguments.callee.caller && arguments.callee.caller.name ) - message += ' - '+arguments.callee.caller.caller.name+'()'; - - if ( !debugReq ) - { - if ( Browser ) - debugParms = "view=request&request=log&task=create&browser[name]="+Browser.name+"&browser[version]="+Browser.version+"&browser[platform]="+(Browser.Platform?Browser.Platform.name:'unknown'); - else - debugParms = "view=request&request=log&task=create&browser[name]=unknown&browser[version]=unknown&browser[platform]=unknown"; - debugReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'chain' } ); + if ( !debugReq ) { + if ( Browser ) { + debugParms = "view=request&request=log&task=create&browser[name]="+Browser.name+"&browser[version]="+Browser.version+"&browser[platform]="+(Browser.Platform?Browser.Platform.name:'unknown'); + } else { + debugParms = "view=request&request=log&task=create&browser[name]=unknown&browser[version]=unknown&browser[platform]=unknown"; } - var requestParms = debugParms; - requestParms += "&level="+level+"&message="+encodeURIComponent(message); - if ( file ) - requestParms += "&file="+file; - else if ( location.search ) { - //location.search is the querystring part, so ?blah=blah but there is almost never any value to this - requestParms += "&file="+location.search; - } - if ( line ) - requestParms += "&line="+line; - debugReq.send( requestParms ); + debugReq = new Request.JSON( {url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'chain'} ); + } + var requestParms = debugParms; + requestParms += "&level="+level+"&message="+encodeURIComponent(message); + if ( file ) { + requestParms += "&file="+file; + } else if ( location.search ) { + //location.search is the querystring part, so ?blah=blah but there is almost never any value to this + requestParms += "&file="+location.search; + } + if ( line ) { + requestParms += "&line="+line; + } + debugReq.send( requestParms ); } -function Panic( message ) -{ - console.error( message ); - logReport( "PNC", message ); - alert( "PANIC: "+message ); +function Panic( message ) { + console.error( message ); + logReport( "PNC", message ); + alert( "PANIC: "+message ); } -function Fatal( message ) -{ - console.error( message ); - logReport( "FAT", message ); - alert( "FATAL: "+message ); +function Fatal( message ) { + console.error( message ); + logReport( "FAT", message ); + alert( "FATAL: "+message ); } -function Error( message ) -{ - console.error( message ); - logReport( "ERR", message ); +function Error( message ) { + console.error( message ); + logReport( "ERR", message ); } -function Warning( message ) -{ - console.warn( message ); - logReport( "WAR", message ); +function Warning( message ) { + console.warn( message ); + logReport( "WAR", message ); } -function Info( message ) -{ - console.info( message ); - logReport( "INF", message ); +function Info( message ) { + console.info( message ); + logReport( "INF", message ); } -function Debug( message ) -{ - console.debug( message ); - //logReport( "DBG", message ); +function Debug( message ) { + console.debug( message ); + //logReport( "DBG", message ); } -function Dump( value, label ) -{ - if ( label ) - console.debug( label+" => " ); - console.debug( value ); +function Dump( value, label ) { + if ( label ) { + console.debug( label+" => " ); + } + console.debug( value ); } window.onerror = - function( message, url, line ) - { - logReport( "ERR", message, url, line ); + function( message, url, line ) { + logReport( "ERR", message, url, line ); }; + +window.addEventListener("securitypolicyviolation", function logCSP(evt) { + var level = evt.disposition == "enforce" ? "ERR" : "DBG"; + var message = evt.blockedURI + " violated CSP " + evt.violatedDirective; + if (evt.sample) { + message += " (Sample: " + evt.sample + ")"; + } + logReport(level, message, evt.sourceFile, evt.lineNumber); +}); diff --git a/web/js/mootools.ext.js b/web/js/mootools.ext.js index bc2f276e3..76c55fb38 100644 --- a/web/js/mootools.ext.js +++ b/web/js/mootools.ext.js @@ -6,15 +6,11 @@ var zmMooToolsVersion = '1.3.2'; /* * Firstly, lets check that mootools has been included and thus is present */ -if ( typeof(MooTools) == "undefined" ) -{ - alert( "MooTools not found! Please check that it was installed correctly in ZoneMinder web root." ); -} -else -{ - /* Version check */ - if ( MooTools.version < zmMooToolsVersion ) - { - alert( "MooTools version "+MooTools.version+" found.\nVersion "+zmMooToolsVersion+" required, please check that it was installed correctly in ZoneMinder web root." ); - } +if ( typeof(MooTools) == "undefined" ) { + alert( "MooTools not found! Please check that it was installed correctly in ZoneMinder web root." ); +} else { + /* Version check */ + if ( MooTools.version < zmMooToolsVersion ) { + alert( "MooTools version "+MooTools.version+" found.\nVersion "+zmMooToolsVersion+" required, please check that it was installed correctly in ZoneMinder web root." ); + } } diff --git a/web/js/overlay.js b/web/js/overlay.js index 9738487b0..6517d2d7d 100644 --- a/web/js/overlay.js +++ b/web/js/overlay.js @@ -1,133 +1,118 @@ var Overlay = new Class({ - Implements: [Options, Events], - initialize: function( id, options ) - { - this.setOptions( options ); + Implements: [Options, Events], + initialize: function( id, options ) { + this.setOptions( options ); - this.mask = new Mask( document.body, { 'maskMargins': false, 'class': 'overlayMask' } ); + this.mask = new Mask( document.body, {'maskMargins': false, 'class': 'overlayMask'} ); - this.id = id?id:'overlay'; - if ( typeOf(this.id) == 'string' ) - { - if ( $(this.id) ) - this.element = $(this.id); - } - else - { - this.element = this.id; - this.id = this.element.get('id'); - } - if ( !this.element ) - { - this.element = new Element( 'div', { 'id': this.id, 'class': 'overlay', 'styles': { 'display': 'none' } } ); - if ( this.options.title || this.options.buttons ) - { - var overlayHeader = new Element( 'div', { 'class': 'overlayHeader' } ); - if ( this.options.title ) - { - var overlayTitle = new Element( 'div', { 'class': 'overlayTitle', 'text': this.options.title } ); - overlayHeader.grab( overlayTitle ); - } - if ( this.options.buttons ) - { - var overlayToolbar = new Element( 'div', { 'class': 'overlayToolbar' } ); - this.options.buttons.each( - function( button ) - { - var overlayButton = new Element( 'button', { 'text': button.text } ); - if ( button.id ) - overlayButton.setProperty( 'id', button.id ); - if ( button.events ) - overlayButton.set( 'events', events ); - overlayToolbar.grab( overlayButton ); - } - ); - overlayHeader.grab( overlayTitle ); - } - this.element.grab( overlayHeader ); - var overlayBody = new Element( 'div', { 'class': 'overlayBody' } ); - var overlayContent = new Element( 'div', { 'class': 'overlayContent' } ); - overlayContent.grab( this.options.content ); - overlayBody.grab( overlayContent ); - this.element.grab( overlayBody ); - } - this.target = document.id(this.options.target) || document.id(document.body); - this.element.inject( this.target ); - } - }, - show: function() - { - this.mask.show(); - $(window).addEvent( 'resize', this.update.bind(this) ); - $(window).addEvent( 'scroll', this.update.bind(this) ); - this.element.tween( 'opacity', [0, 1.0] ); - this.element.show(); - this.element.position(); - this.mask.position(); - }, - hideComplete: function() - { - $(window).removeEvent( 'resize', this.update.bind(this) ); - $(window).removeEvent( 'scroll', this.update.bind(this) ); - this.element.hide(); - this.mask.hide(); - }, - hide: function() - { - new Fx.Tween( this.element, { duration: 400, transition: Fx.Transitions.Sine, onComplete: this.hideComplete.bind(this) } ).start( 'opacity', 1.0, 0 ); - }, - update: function() - { - this.element.position(); - this.mask.position(); - }, - showAnimation:function() - { - showOverlay(); - - //console.log( "Showing overlay loading" ); - if ( !this.loading ) - { - this.loading = new Element( 'div', { 'id': 'loading'+this.key, 'styles': { 'display':'none' } } ); - this.loading.grab( this.loadingImage ); - document.body.grab( this.loading ); - } - updateOverlayLoading(); - this.loading.setStyle( 'display', 'block' ); - $(window).addEvent( 'resize', this.update.bind(this) ); - $(window).addEvent( 'scroll', this.update.bind(this) ); - }, - hideAnimation:function() - { - $(window).removeEvent( 'resize', this.update.bind(this) ); - $(window).removeEvent( 'scroll', this.update.bind(this) ); - if ( this.loading ) - this.loading.setStyle( 'display', 'none' ); + this.id = id?id:'overlay'; + if ( typeOf(this.id) == 'string' ) { + if ( $(this.id) ) { + this.element = $(this.id); + } + } else { + this.element = this.id; + this.id = this.element.get('id'); } + if ( !this.element ) { + this.element = new Element( 'div', {'id': this.id, 'class': 'overlay', 'styles': {'display': 'none'}} ); + if ( this.options.title || this.options.buttons ) { + var overlayHeader = new Element( 'div', {'class': 'overlayHeader'} ); + if ( this.options.title ) { + var overlayTitle = new Element( 'div', {'class': 'overlayTitle', 'text': this.options.title} ); + overlayHeader.grab( overlayTitle ); + } + if ( this.options.buttons ) { + var overlayToolbar = new Element( 'div', {'class': 'overlayToolbar'} ); + this.options.buttons.each( + function( button ) { + var overlayButton = new Element( 'button', {'text': button.text} ); + if ( button.id ) { + overlayButton.setProperty( 'id', button.id ); + } + if ( button.events ) { + overlayButton.set( 'events', events ); + } + overlayToolbar.grab( overlayButton ); + } + ); + overlayHeader.grab( overlayTitle ); + } + this.element.grab( overlayHeader ); + var overlayBody = new Element( 'div', {'class': 'overlayBody'} ); + var overlayContent = new Element( 'div', {'class': 'overlayContent'} ); + overlayContent.grab( this.options.content ); + overlayBody.grab( overlayContent ); + this.element.grab( overlayBody ); + } + this.target = document.id(this.options.target) || document.id(document.body); + this.element.inject( this.target ); + } + }, + show: function() { + this.mask.show(); + window.addEventListener( 'resize', this.update.bind(this) ); + window.addEventListener( 'scroll', this.update.bind(this) ); + this.element.tween( 'opacity', [0, 1.0] ); + this.element.show(); + this.element.position(); + this.mask.position(); + }, + hideComplete: function() { + this.element.hide(); + this.mask.hide(); + }, + hide: function() { + new Fx.Tween( this.element, {duration: 400, transition: Fx.Transitions.Sine, onComplete: this.hideComplete.bind(this)} ).start( 'opacity', 1.0, 0 ); + }, + update: function() { + this.element.position(); + this.mask.position(); + }, + showAnimation: function() { + showOverlay(); + + //console.log( "Showing overlay loading" ); + if ( !this.loading ) { + this.loading = new Element( 'div', {'id': 'loading'+this.key, 'styles': {'display': 'none'}} ); + this.loading.grab( this.loadingImage ); + document.body.grab( this.loading ); + } + updateOverlayLoading(); + this.loading.setStyle( 'display', 'block' ); + window.addEventListener( 'resize', this.update.bind(this) ); + window.addEventListener( 'scroll', this.update.bind(this) ); + }, + hideAnimation: function() { + if ( this.loading ) { + this.loading.setStyle( 'display', 'none' ); + } + } }); -function setupOverlays() -{ - try { +function setupOverlays() { + try { $$('.overlay').each( - function( overlay ) - { - overlay.element = new Overlay( overlay.get('id') ); - overlay.getElements('.overlayCloser').each( - function( closer ) - { - closer.addEvent( 'click', function() { overlay.element.hide(); } ); - } - ); - overlay.overlayShow = function() { overlay.element.show(); }; - overlay.overlayHide = function() { overlay.element.hide(); }; + function( overlay ) { + overlay.element = new Overlay( overlay.get('id') ); + overlay.getElements('.overlayCloser').each( + function( closer ) { + closer.addEvent( 'click', function() { + overlay.element.hide(); + } ); + } + ); + overlay.overlayShow = function() { + overlay.element.show(); + }; + overlay.overlayHide = function() { + overlay.element.hide(); + }; } ); - } - catch ( e ) - { - alert( e ); - } + } catch ( e ) { + alert( e ); + } } -window.addEvent( 'domready', setupOverlays ); +window.addEventListener( 'DOMContentLoaded', setupOverlays ); diff --git a/web/lang/ba_ba.php b/web/lang/ba_ba.php new file mode 100644 index 000000000..96a3ca4d1 --- /dev/null +++ b/web/lang/ba_ba.php @@ -0,0 +1,996 @@ + 'Dnevnik', + 'DateTime' => 'Datum/Vrijeme', + 'Component' => 'Komponenta', + 'Pid' => 'PID', + 'Level' => 'Nivo', + 'Message' => 'Poruka', + 'Line' => 'Linija', + 'More' => 'Više', + 'Clear' => 'Očisti', + '24BitColour' => '24 bitne boje', + '32BitColour' => '32 bitne boje', + '8BitGrey' => '8 bit siva nijansa', + 'Action' => 'Action', + 'Actual' => 'Stvarno', + 'AddNewControl' => 'Dodaj kontrolu', + 'AddNewMonitor' => 'Dodaj monitor', + 'AddNewServer' => 'Dodaj novi server', + 'AddNewStorage' => 'Dodaj novi disk', + 'AddNewUser' => 'Dodaj novog korisnika', + 'AddNewZone' => 'Dodaj novu zonu', + 'Alarm' => 'Alarm', + 'AlarmBrFrames' => 'Alarm
Sličice', + 'AlarmFrame' => 'Alarm sličica', + 'AlarmFrameCount' => 'Brzina snimanja alarma (u frejmovima)', + 'AlarmLimits' => 'Alarm limiti', + 'AlarmMaximumFPS' => 'Alarm Max SPS', + 'AlarmPx' => 'Alarm Px', + 'AlarmRefImageBlendPct' => 'Alarm Reference Image Blend %ge', + 'AlarmRGBUnset' => 'Morate postaviti RGB boju za alarm', + 'Alert' => 'Uzbuna', + 'All' => 'Sve', + 'AnalysisFPS' => 'Analiza frejmova', + 'AnalysisUpdateDelay' => 'Analysis Update Delay', + 'Apply' => 'Primjeni', + 'ApplyingStateChange' => 'Primjenjujem promjenu stanja', + 'ArchArchived' => 'Samo arhivirano', + 'Archive' => 'Arhiva', + 'Archived' => 'Ahivirano', + 'ArchUnarchived' => 'Samo nearhivirano', + 'Area' => 'Oblast', + 'AreaUnits' => 'Oblast (px/%)', + 'AttrAlarmFrames' => 'Alarm frejmovi', + 'AttrArchiveStatus' => 'Status arhive', + 'AttrAvgScore' => 'Prosj. score', + 'AttrCause' => 'Uzrok', + 'AttrStartDate' => 'Pocetni datum', + 'AttrEndDate' => 'Krajnji datum', + 'AttrStartDateTime' => 'Pocetni Datum/Vrijeme', + 'AttrEndDateTime' => 'Krajnji Datum/Vrijeme', + 'AttrDiskSpace' => 'Disk prostor', + 'AttrDiskBlocks' => 'Disk blokovi', + 'AttrDiskPercent' => 'Disk procentualno', + 'AttrDuration' => 'Trajanje', + 'AttrFrames' => 'Frejmovi', + 'AttrId' => 'Id', + 'AttrMaxScore' => 'Max. Score', + 'AttrMonitorId' => 'ID Kamere', + 'AttrMonitorName' => 'Naziv Kamere', + 'AttrStorageArea' => 'Storage Area', + 'AttrFilterServer' => 'Server Filter je pokrenut na', + 'AttrMonitorServer' => 'Server Monitor je pokrenut na', + 'AttrStorageServer' => 'Server Hosting Storage', + 'AttrStateId' => 'Status', + 'AttrName' => 'Naziv', + 'AttrNotes' => 'Bilješke', + 'AttrSystemLoad' => 'Opterećenje sistema', + 'AttrStartTime' => 'Vrijeme početka', + 'AttrEndTime' => 'Vrijeme završetka', + 'AttrTotalScore' => 'Ukupan score', + 'AttrStartWeekday' => 'Početni dan', + 'AttrEndWeekday' => 'Krajnji dan', + 'Auto' => 'Automatski', + 'AutoStopTimeout' => 'Auto Stop Timeout', + 'Available' => 'Dostupno', + 'AvgBrScore' => 'Avg.
Score', + 'Available' => 'Dostupno', + 'Background' => 'Pozadina', + 'BackgroundFilter' => 'Pokreni filter u pozadini', + 'BadAlarmFrameCount' => 'Brojač alarm frejmova mora biti tipa integer počevši od jedan ili više', + 'BadAlarmMaxFPS' => 'Max FPS za alarm mora biti pozitivan cjeli broj ili broj sa pomičnim zarezom', + 'BadAnalysisFPS' => 'Broj frejmova za analitiku mora pozitivan cjeli broj ili broj sa pomičnim zarezom', + 'BadAnalysisUpdateDelay'=> 'Vrijeme zadrške analitike mora biti broj od nula ili više', + 'BadChannel' => 'Kanal mora biti postavljen na cjeli broj nula ili više', + 'BadDevice' => 'Uredaj mora biti postavljen na validnu vrijednost', + 'BadFormat' => 'Format mora biti postavljen na validnu vrijenost', + 'BadFPSReportInterval' => 'FPS report interval buffer count must be an integer of 0 or more', + 'BadFrameSkip' => 'Frame skip count must be an integer of zero or more', + 'BadMotionFrameSkip' => 'Motion Frame skip count must be an integer of zero or more', + 'BadHeight' => 'Height must be set to a valid value', + 'BadHost' => 'Host must be set to a valid ip address or hostname, do not include http://', + 'BadImageBufferCount' => 'Image buffer size must be an integer of 10 or more', + 'BadLabelX' => 'Label X co-ordinate must be set to an integer of zero or more', + 'BadLabelY' => 'Label Y co-ordinate must be set to an integer of zero or more', + 'BadMaxFPS' => 'Maximum FPS must be a positive integer or floating point value', + 'BadNameChars' => 'Names may only contain alphanumeric characters plus spaces, hyphen and underscore', + 'BadPalette' => 'Palette must be set to a valid value', + 'BadColours' => 'Target colour must be set to a valid value', + 'BadPath' => 'Path must be set to a valid value', + 'BadPort' => 'Port must be set to a valid number', + 'BadPostEventCount' => 'Post event image count must be an integer of zero or more', + 'BadPreEventCount' => 'Pre event image count must be at least zero, and less than image buffer size', + 'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer', + 'BadSectionLength' => 'Section length must be an integer of 30 or more', + 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', + 'BadStreamReplayBuffer' => 'Stream replay buffer must be an integer of zero or more', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', + 'BadWarmupCount' => 'Warmup frames must be an integer of zero or more', + 'BadWebColour' => 'Web colour must be a valid web colour string', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', + 'BadWidth' => 'Width must be set to a valid value', + 'Bandwidth' => 'Propusnost', + 'BandwidthHead' => 'propusnost', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing + 'BlobPx' => 'Blob Px', + 'Blobs' => 'Blobs', + 'BlobSizes' => 'Blob velicine', + 'Brightness' => 'Svjetloća', + 'Buffer' => 'Bufer', + 'Buffers' => 'Buferi', + 'CanAutoFocus' => 'Podržava Auto fokusiranje', + 'CanAutoGain' => 'Podržava Auto pojačanje', + 'CanAutoIris' => 'Podržava Auto blenda', + 'CanAutoWhite' => 'Podržava Auto balans bijel.', + 'CanAutoZoom' => 'Podržava Auto zum', + 'Cancel' => 'otkaži', + 'CancelForcedAlarm' => 'Otkaži prisilni alarm', + 'CanFocusAbs' => 'Podržava Abs fokus', + 'CanFocus' => 'Podržava Fokus', + 'CanFocusCon' => 'Podržava Kontinuirani fokus', + 'CanFocusRel' => 'Podržava Relativni fokus', + 'CanGainAbs' => 'Podržava Aps. pojačanje', + 'CanGain' => 'Podržava Pojačanje ', + 'CanGainCon' => 'Podržava Kontinuirano pojačanje', + 'CanGainRel' => 'Podržava Relativno pojačanje', + 'CanIrisAbs' => 'Podržava Aps. blenda', + 'CanIris' => 'Podržava Blenda', + 'CanIrisCon' => 'Podržava kontinuirana blenda', + 'CanIrisRel' => 'Podržava Relativna blenda', + 'CanMoveAbs' => 'Podržava Aps. kretanje', + 'CanMove' => 'Podržava Kretanje', + 'CanMoveCon' => 'Podržava Kontinuirano kretanje', + 'CanMoveDiag' => 'Podržava Dijagonalno kretanje', + 'CanMoveMap' => 'Podržava Mapirano kretanje', + 'CanMoveRel' => 'Podržava Relativno kretanje', + 'CanPan' => 'Podržava Pomak' , + 'CanReset' => 'PodržavaReset', + 'CanReboot' => 'Can Reboot', + 'CanSetPresets' => 'Podržava presetove', + 'CanSleep' => 'Podržava Sleep', + 'CanTilt' => 'Podržava nagib', + 'CanWake' => 'Podržava Wake', + 'CanWhiteAbs' => 'Podržava Aps. balans bijele boje', + 'CanWhiteBal' => 'Podržava balans bijel.', + 'CanWhite' => 'Podržava bijelu', + 'CanWhiteCon' => 'Podržava kont. balans bijele boje', + 'CanWhiteRel' => 'Podržava relativ. balans bijele boje', + 'CanZoomAbs' => 'Podržava Aps. zoom', + 'CanZoom' => 'Podržava Zoom', + 'CanZoomCon' => 'Podržava kontinuirani Zoom', + 'CanZoomRel' => 'Podržava Relativni zoom', + 'CaptureHeight' => 'Visina slike', + 'CaptureMethod' => 'Metoda snimanja', + 'CaptureResolution' => 'Snimi rezoluciju', + 'CapturePalette' => 'Paleta boja', + 'CaptureWidth' => 'Širina slike', + 'Cause' => 'Uzrok', + 'CheckMethod' => 'Metoda provjere alarma', + 'ChooseDetectedCamera' => 'Odaberi otkrivenu kameru', + 'ChooseFilter' => 'Odaberi filter', + 'ChooseLogFormat' => 'Odaberi dugi format', + 'ChooseLogSelection' => 'Odaberi dugu selekciju', + 'ChoosePreset' => 'Odaberi preset', + 'CloneMonitor' => 'Kloniraj', + 'Close' => 'Zatvori', + 'Colour' => 'Bojs', + 'Command' => 'Komanda', + 'ConcurrentFilter' => 'Istovremeno pokreni filter', + 'Config' => 'Postavke', + 'ConfiguredFor' => 'Podešeno za', + 'ConfirmDeleteEvents' => 'Sigurni ste da želite izbrisati odabrane događaje?', + 'ConfirmPassword' => 'Potvrdi lozinku', + 'ConjAnd' => 'i', + 'ConjOr' => 'ili', + 'Console' => 'Konzola', + 'ContactAdmin' => 'Molimo konkatirajte svog administratora za detalje.', + 'Continue' => 'Nastavi', + 'Contrast' => 'Kontrast', + 'ControlAddress' => 'Kontrolna adresa', + 'ControlCap' => 'Control Capability', + 'ControlCaps' => 'Control Capabilities', + 'Control' => 'PTZ kontole', + 'ControlDevice' => 'Kontroliši uređaj', + 'Controllable' => 'Moguće kontrolisati', + 'ControlType' => 'Tipa kontrole', + 'Current' => 'Tekuće', + 'Cycle' => 'Kruži', + 'CycleWatch' => 'Kružni prikaz', + 'Day' => 'Dan', + 'Debug' => 'Debug', + 'DefaultRate' => 'Podrazumjevana stopa', + 'DefaultScale' => 'Podrazumjevani razmjer', + 'DefaultView' => 'Podrazumjevani prikaz', + 'Deinterlacing' => 'Deinterlacing', + 'RTSPDescribe' => 'Use RTSP Response Media URL', + 'Delay' => 'Zadrška', + 'DeleteAndNext' => 'Izbriši & Sljedeće', + 'DeleteAndPrev' => 'Izbriši & Preth', + 'Delete' => 'Izbriši', + 'DeleteSavedFilter' => 'Izbriši spremljeni filter', + 'Description' => 'Opis', + 'DetectedCameras' => 'Detektovane kamere:', + 'DetectedProfiles' => 'Otkriveni profili', + 'DeviceChannel' => 'Kanal', + 'DeviceFormat' => 'Sistem boja', + 'DeviceNumber' => 'Broj uređaja', + 'DevicePath' => 'Putanja uređaja', + 'Device' => 'Uređaj', + 'Devices' => 'Uređaji', + 'Dimensions' => 'Dimenzije', + 'DisableAlarms' => 'Onemogući alarme', + 'Disk' => 'Disk', + 'Display' => 'Prikaz', + 'Displaying' => 'Prikazujem', + 'DonateAlready' => 'Ne, već sam napravio donaciju.', + 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', + 'Donate' => 'Molimo donirajte', + 'DonateRemindDay' => 'Ne još, podsjetime za 1 dan', + 'DonateRemindHour' => 'Ne još, podsjetime za 1 sat', + 'DonateRemindMonth' => 'Ne još, podsjeti me za jedan mjesec', + 'DonateRemindNever' => 'Ne, ne želim donirati, nemoj me više podsjećati.', + 'DonateRemindWeek' => 'Ne još, podsjeti me za sedam dana.', + 'DonateYes' => 'Da, želim da doniram sada.', + 'DoNativeMotionDetection'=> 'Nativna detekcija pokreta', + 'Download' => 'Preuzmi', + 'DuplicateMonitorName' => 'Dupliciraj ime monitora', + 'Duration' => 'Trajanje', + 'Edit' => 'Uredi', + 'EditLayout' => 'Uredi raspored', + 'Email' => 'Email', + 'EnableAlarms' => 'Omogući alarme', + 'Enabled' => 'Omogućeno', + 'EnterNewFilterName' => 'Unesi novo ime za filter', + 'ErrorBrackets' => 'Greška, provjerite da li imate jednak broj otvorenih i zatvorenih zagrada.', + 'Error' => 'Greška', + 'ErrorValidValue' => 'Greška, osigurajte se da svi pojmovi imaju valide vrijednosti', + 'Etc' => 'itd', + 'Event' => 'Događaj', + 'EventFilter' => 'Filter događaja', + 'EventId' => 'ID događaja', + 'EventName' => 'Naziv događaja', + 'EventPrefix' => 'Prefiks događaja', + 'Events' => 'Događaji', + 'Exclude' => 'Isključi', + 'Execute' => 'Izvrši', + 'ExportDetails' => 'Izvezi detalje o događaju', + 'Exif' => 'Umetni EXIF podatke u sliku', + 'Export' => 'Izvezi', + 'DownloadVideo' => 'Preuzmi video', + 'GenerateDownload' => 'Generiši preuzimanje', + 'ExportFailed' => 'Izvoz nije uspio', + 'ExportFormat' => 'Format za izvoz', + 'ExportFormatTar' => 'Tar', + 'ExportFormatZip' => 'Zip', + 'ExportFrames' => 'Izvezi detalje frejma', + 'ExportImageFiles' => 'Izvezi slike', + 'ExportLog' => 'Izvezi zapisnik', + 'Exporting' => 'Izvozim', + 'ExportMiscFiles' => 'Izvezi druge fajlove (ukoliko postoje)', + 'ExportOptions' => 'Opcije izvoženja', + 'ExportSucceeded' => 'Izvoz uspio', + 'ExportVideoFiles' => 'Izvezi video fileove (ukoliko postoje)', + 'Far' => 'Far', + 'FastForward' => 'Naprijed', + 'Feed' => 'Feed', + 'Ffmpeg' => 'Ffmpeg', + 'File' => 'File', + 'FilterArchiveEvents' => 'Arhiviraj pronađeno', + 'FilterUpdateDiskSpace' => 'Ažuriraj korišteni prostor na disku', + 'FilterDeleteEvents' => 'Izbriši sve pronađeno', + 'FilterMoveEvents' => 'Premjesti pronađeno', + 'FilterEmailEvents' => 'Pošalji detalje mailom', + 'FilterExecuteEvents' => 'Izvrši sljededeću komandu', + 'FilterLog' => 'Filtriraj zapis', + 'FilterMessageEvents' => 'Message details of all matches', + 'FilterPx' => 'Filter Px', + 'Filter' => 'Filter', + 'Filters' => 'Filteri', + 'FilterUnset' => 'Morate navesti širinu i visinu filtera', + 'FilterUploadEvents' => 'Učitaj sve događaje', + 'FilterVideoEvents' => 'Napravi video', + 'First' => 'Prvi', + 'FlippedHori' => 'Zaokrenuto horizontalno', + 'FlippedVert' => 'Zaokrenuto vertikalno', + 'FnNone' => 'nijedan', // Added 2013.08.16. + 'FnMonitor' => 'Monitor', // Added 2013.08.16. + 'FnModect' => 'Modect', // Added 2013.08.16. + 'FnRecord' => 'Record', // Added 2013.08.16. + 'FnMocord' => 'Mocord', // Added 2013.08.16. + 'FnNodect' => 'Nodect', // Added 2013.08.16. + 'Focus' => 'Fokus', + 'ForceAlarm' => 'Prisilni alarm', + 'Format' => 'Format', + 'FPS' => 'fps', + 'FPSReportInterval' => 'FPS Report Interval', + 'Frame' => 'Frame', + 'FrameId' => 'Frame Id', + 'FrameRate' => 'Frame Rate', + 'Frames' => 'Frejmovi', + 'FrameSkip' => 'Preskoči frejm', + 'MotionFrameSkip' => 'Motion Frame Skip', + 'FTP' => 'FTP', + 'Func' => 'Func', + 'Function' => 'Funkcija', + 'Gain' => 'Pojačanje', + 'General' => 'Opšte', + 'GenerateVideo' => 'Generiši video', + 'GeneratingVideo' => 'Generiši video', + 'GoToZoneMinder' => 'Idi na ZoneMinder.com', + 'Grey' => 'Siva', + 'Group' => 'Grupa', + 'Groups' => 'Grupe', + 'HasFocusSpeed' => 'Posjeduje brzo fokusiranja', + 'HasGainSpeed' => 'Posjeduje brzo pojačanja', + 'HasHomePreset' => 'Has Home Preset', + 'HasIrisSpeed' => 'Posjeduje brzu blendu', + 'HasPanSpeed' => 'Posjeduje brzi pomak', + 'HasPresets' => 'Posjeduje pre-setove', + 'HasTiltSpeed' => 'Posjeduje brzi nagiba', + 'HasTurboPan' => 'Posjeduje turbo pomak', + 'HasTurboTilt' => 'Posjeduje turbo nagib', + 'HasWhiteSpeed' => 'Posjeduje brzo podeš.bijele', + 'HasZoomSpeed' => 'Posjeduje brzi zoom', + 'HighBW' => 'High B/W', + 'High' => 'veliku', + 'Home' => 'Početna', + 'Hostname' => 'Hostname', + 'Hour' => 'Sat', + 'Hue' => 'Nijansa', + 'Id' => 'Id', + 'Idle' => 'Na čekanju', + 'Ignore' => 'Zanemari', + 'ImageBufferSize' => 'Veličina slikovnog bufera (u frejmovima)', + 'Image' => 'Slika', + 'Images' => 'Slike', + 'Include' => 'Uključi', + 'In' => 'U', + 'Inverted' => 'Invertirano', + 'Iris' => 'Blenda', + 'KeyString' => 'Key String', + 'Label' => 'Oznaka', + 'Language' => 'Jezik', + 'Last' => 'Zadnje', + 'Layout' => 'Raspored', + 'Libvlc' => 'Libvlc', + 'LimitResultsPost' => 'results only', // This is used at the end of the phrase 'Limit to first N results only' + 'LimitResultsPre' => 'Limit to first', // This is used at the beginning of the phrase 'Limit to first N results only' + 'LinkedMonitors' => 'Povezani monitori', + 'List' => 'Popis', + 'ListMatches' => 'Prikaži pronađeno', + 'Load' => 'Opterećenje', + 'Local' => 'Lokalno', + 'Log' => 'Zapis', + 'Logs' => 'Zapisi', + 'Logging' => 'Dnevnik događaja', + 'LoggedInAs' => 'Prijavljen kao', + 'LoggingIn' => 'Prijavljujem', + 'Login' => 'prijava', + 'Logout' => 'odjava', + 'LowBW' => 'Low B/W', + 'Low' => 'nisku', + 'Main' => 'Glavno', + 'Man' => 'Man', + 'Manual' => 'Ručno', + 'Mark' => 'Označi', + 'MaxBandwidth' => 'Max propusnost', + 'MaxBrScore' => 'Max.
Score', + 'MaxFocusRange' => 'Max raspon fokusa', + 'MaxFocusSpeed' => 'Max brzina fokusa', + 'MaxFocusStep' => 'Max korak fokusa', + 'MaxGainRange' => 'Max raspon pojačanja', + 'MaxGainSpeed' => 'Max brzina pojačanja', + 'MaxGainStep' => 'Max korak pojačanja', + 'MaximumFPS' => 'Maximum FPS', + 'MaxIrisRange' => 'Max raspon blende', + 'MaxIrisSpeed' => 'Max brzina blende', + 'MaxIrisStep' => 'Max korak blende', + 'Max' => 'Max', + 'MaxPanRange' => 'Max raspon pomaka', + 'MaxPanSpeed' => 'Max brzina pomaka', + 'MaxPanStep' => 'Max korak pomaka', + 'MaxTiltRange' => 'Max raspon nagiba', + 'MaxTiltSpeed' => 'Max brzina nagiba', + 'MaxTiltStep' => 'Max korak nagiba', + 'MaxWhiteRange' => 'Max raspon bijele', + 'MaxWhiteSpeed' => 'Max brzina bijele', + 'MaxWhiteStep' => 'Max korak bijele', + 'MaxZoomRange' => 'Max raspon zumiranja', + 'MaxZoomSpeed' => 'Max brzina zumiranja', + 'MaxZoomStep' => 'Max korak zumiranja', + 'MediumBW' => 'Medium B/W', + 'Medium' => 'srednju', + 'MinAlarmAreaLtMax' => 'Min područje alarma mora biti manje od maksimalnog', + 'MinAlarmAreaUnset' => 'Morate zadati minimalni broj alarm piksela', + 'MinBlobAreaLtMax' => 'Min blob područje mora biti manje od maksimalnog', + 'MinBlobAreaUnset' => 'Morate zadati minimalni broj blob piksela', + 'MinBlobLtMinFilter' => 'Min blob oblast mora biti manja ili jednaka minimalnoj oblasti filtera', + 'MinBlobsLtMax' => 'Min blob mora biti manji od maksimalne', + 'MinBlobsUnset' => 'morate zadati minimalni broj blob-ova', + 'MinFilterAreaLtMax' => 'Minimalna oblast filtera mora biti manja od maksimalne', + 'MinFilterAreaUnset' => 'Morate zadati minimalni broj filter piksela', + 'MinFilterLtMinAlarm' => 'Min oblast filtera mora biti manja ili jednaka minimalnoj oblasti alarmne oblasti', + 'MinFocusRange' => 'Min raspon fokusiranja', + 'MinFocusSpeed' => 'Min brzina fokusiranja', + 'MinFocusStep' => 'Min korak fokusiranja', + 'MinGainRange' => 'Min raspon pojačanja', + 'MinGainSpeed' => 'Min brzina pojačanja', + 'MinGainStep' => 'Min korak pojačanja', + 'MinIrisRange' => 'Min raspon blende', + 'MinIrisSpeed' => 'Min brzina blende', + 'MinIrisStep' => 'Min korak blende', + 'MinPanRange' => 'Min raspon pomaka', + 'MinPanSpeed' => 'Min brzina pomaka', + 'MinPanStep' => 'Min korak pomaka', + 'MinPixelThresLtMax' => 'Min prag piksela mora biti manji od maksimalnog', + 'MinPixelThresUnset' => 'Morate zadati minimalni prag piksela', + 'MinTiltRange' => 'Min Tilt Range', + 'MinTiltSpeed' => 'Min Tilt Speed', + 'MinTiltStep' => 'Min Tilt Step', + 'MinWhiteRange' => 'Min raspon bijelog balansa', + 'MinWhiteSpeed' => 'Min brzina bijelog balansa', + 'MinWhiteStep' => 'Min White Bal. Step', + 'MinZoomRange' => 'Min raspon zumiranja', + 'MinZoomSpeed' => 'Min brzina zumiranja', + 'MinZoomStep' => 'Min korak zumiranja', + 'Misc' => 'Razno', + 'Mode' => 'Modus', + 'MonitorIds' => 'Monitor Ids', + 'Monitor' => 'Monitor', + 'MonitorPresetIntro' => 'Odaberite odgovarajuće pre-setove sa popisa.

Imajte u vidu da ovo može prepisati bilo koju vrijednost koja postoji za odabrane monitore.

', + 'MonitorPreset' => 'Monitor Preset', + 'MonitorProbeIntro' => 'Donji popis prikazuje otkrivene analogne i mrežne kamere, te da li se iste već koriste i da li su dostupne.

Odaberite željenu kameru sa donjeg popisa.

Imajte u vidu da ovo može prepisati bilo koju vrijednost koja postoji za odabrane monitore.

', + 'MonitorProbe' => 'Detektuj kameru', + 'Monitors' => 'Monitori', + 'Montage' => 'Montage', + 'MontageReview' => 'Montage pregled', + 'Month' => 'Mjesec', + 'Move' => 'Pomjeri', + 'MtgDefault' => 'Podrazumjevano', // Added 2013.08.15. + 'Mtg2widgrd' => '2-struka rešetka', // Added 2013.08.15. + 'Mtg3widgrd' => '3-struka rešetka', // Added 2013.08.15. + 'Mtg4widgrd' => '4-struka rešetka', // Added 2013.08.15. + 'Mtg3widgrx' => '3-wide grid, scaled, enlarge on alarm', // Added 2013.08.15. + 'MustBeGe' => 'mora biti veće ili jednako', + 'MustBeLe' => 'mora biti manje ili jednako', + 'MustConfirmPassword' => 'Morate potvrditi lozinku', + 'MustSupplyPassword' => 'Morate unjeti lozinku', + 'MustSupplyUsername' => 'Morate unjeti korisničko ime', + 'Name' => 'Ime', + 'Near' => 'Blizu', + 'Network' => 'Mreža', + 'NewGroup' => 'Nova grupa', + 'NewLabel' => 'Nova oznaka', + 'New' => 'Novo', + 'NewPassword' => 'Nova lozinka', + 'NewState' => 'Novi radni modus', + 'NewUser' => 'Novi korisnik', + 'Next' => 'Sljedeće', + 'NoDetectedCameras' => 'Nema otkrivenih kamera', + 'NoDetectedProfiles' => 'Nema otkrivenih profila', + 'NoFramesRecorded' => 'Nije ništa snimljeno za ovaj događaj', + 'NoGroup' => 'Nema grupe', + 'NoneAvailable' => 'Nijedno dostupno', + 'None' => 'Nijedno', + 'No' => 'Ne', + 'Normal' => 'Normalno', + 'NoSavedFilters' => 'NemaSnimljenihFiltera', + 'NoStatisticsRecorded' => 'Nema snimljenih statistika za ovaj događaj', + 'Notes' => 'Bilješke', + 'NumPresets' => 'Num Presets', + 'Off' => 'Isključeno', + 'On' => 'Uključeno', + 'OnvifProbe' => 'ONVIF detekcija', + 'OnvifProbeIntro' => 'The list below shows detected ONVIF cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', + 'OnvifCredentialsIntro' => 'Please supply user name and password for the selected camera.
If no user has been created for the camera then the user given here will be created with the given password.

', + 'Open' => 'Otvori', + 'OpEq' => 'jednako', + 'OpGtEq' => 'veće ili jednako od', + 'OpGt' => 'veće ', + 'OpIn' => 'in set', + 'OpLtEq' => 'manje ili jednako od', + 'OpLt' => 'manje od', + 'OpMatches' => 'matches', + 'OpNe' => 'nije jednako', + 'OpNotIn' => 'nije u ', + 'OpNotMatches' => 'ne poklapa se', + 'OpIs' => 'je', + 'OpIsNot' => 'nije', + 'OptionalEncoderParam' => 'Opcionalni parametri enkodera', + 'OptionHelp' => 'Option Help', + 'OptionRestartWarning' => 'These changes may not come into effect fully\nwhile the system is running. When you have\nfinished making your changes please ensure that\nyou restart ZoneMinder.', + 'Options' => 'Opcije', + 'Order' => 'Redosljed', + 'OrEnterNewName' => 'ili unesi novo ime', + 'Orientation' => 'Orijentacija', + 'Out' => 'Izlaz', + 'OverwriteExisting' => 'Prepiši preko postojećeg', + 'Paged' => 'stranično', + 'PanLeft' => 'Pomak lijevo', + 'Pan' => 'Pomak', + 'PanRight' => 'Pomak desno', + 'PanTilt' => 'Pomak/Nagib', + 'Parameter' => 'Parametar', + 'Password' => 'Lozinka', + 'PasswordsDifferent' => 'Nova i potvrđena lozinka se razlikuju', + 'Paths' => 'Putanje', + 'Pause' => 'Pauza', + 'PhoneBW' => 'Telefon B/W', + 'Phone' => 'Telefon', + 'PixelDiff' => 'Piksel razli.', + 'Pixels' => 'pikseli', + 'PlayAll' => 'play all', + 'Play' => 'Play', + 'Plugins' => 'Plugini', + 'PleaseWait' => 'Molim čekati', + 'Point' => 'Point', + 'PostEventImageBuffer' => 'Br. frejmova poslije događaja', + 'PreEventImageBuffer' => 'Br. frejmova prije događaja', + 'PreserveAspect' => 'Zadrži omjer', + 'Preset' => 'Preset', + 'Presets' => 'Presets', + 'Prev' => 'Preth', + 'Privacy' => 'Privatnost', + 'PrivacyAbout' => 'O', + 'PrivacyAboutText' => 'Since 2002, ZoneMinder has been the premier free and open-source Video Management System (VMS) solution for Linux platforms. ZoneMinder is supported by the community and is managed by those who choose to volunteer their spare time to the project. The best way to improve ZoneMinder is to get involved.', + 'PrivacyContact' => 'Konakt', + 'PrivacyContactText' => 'Please contact us here for any questions regarding our privacy policy or to have your information removed.

For support, there are three primary ways to engage with the community:

Our Github forum is only for bug reporting. Please use our user forum or slack channel for all other questions or comments.

', + 'PrivacyCookies' => 'Kolačići', + 'PrivacyCookiesText' => 'Whether you use a web browser or a mobile app to communicate with the ZoneMinder server, a ZMSESSID cookie is created on the client to uniquely identify a session with the ZoneMinder server. ZmCSS and zmSkin cookies are created to remember your style and skin choices.', + 'PrivacyTelemetry' => 'Telemetry', + 'PrivacyTelemetryText' => 'Because ZoneMinder is open-source, anyone can install it without registering. This makes it difficult to answer questions such as: how many systems are out there, what is the largest system out there, what kind of systems are out there, or where are these systems located? Knowing the answers to these questions, helps users who ask us these questions, and it helps us set priorities based on the majority user base.', + 'PrivacyTelemetryList' => 'The ZoneMinder Telemetry daemon collects the following data about your system:
  • A unique identifier (UUID)
  • City based location is gathered by querying ipinfo.io. City, region, country, latitude, and longitude parameters are saved. The latitude and longitude coordinates are accurate down to the city or town level only!
  • Current time
  • Total number of monitors
  • Total number of events
  • System architecture
  • Operating system kernel, distro, and distro version
  • Version of ZoneMinder
  • Total amount of memory
  • Number of cpu cores
', + 'PrivacyMonitorList' => 'The following configuration parameters from each monitor are collected:
  • Id
  • Name
  • Type
  • Function
  • Width
  • Height
  • Colours
  • MaxFPS
  • AlarmMaxFPS
', + 'PrivacyConclusionText' => 'We are NOT collecting any image specific data from your cameras. We don�t know what your cameras are watching. This data will not be sold or used for any purpose not stated herein. By clicking accept, you agree to send us this data to help make ZoneMinder a better product. By clicking decline, you can still freely use ZoneMinder and all its features.', + 'Probe' => 'Detektuj kameru', + 'ProfileProbe' => 'Stream proba', + 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', + 'Progress' => 'Napredak', + 'Protocol' => 'Protkol', + 'Rate' => 'Stopa', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // added Sep 24 2015 - PP + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', + 'Real' => 'Stvarno', + 'Record' => 'Snimaj', + 'RefImageBlendPct' => 'Reference Image Blend %ge', + 'Refresh' => 'Osvježi', + 'RemoteHostName' => 'Naziv uređaja', + 'RemoteHostPath' => 'Putanja', + 'RemoteHostSubPath' => 'Pod-putanja', + 'RemoteHostPort' => 'Port', + 'RemoteImageColours' => 'Boje slike', + 'RemoteMethod' => 'Metoda', + 'RemoteProtocol' => 'Protokol', + 'Remote' => 'Udaljeno', + 'Rename' => 'Preimenuj', + 'ReplayAll' => 'Svi događaji', + 'ReplayGapless' => 'Gapless Events', + 'Replay' => 'Ponovo odigraj', + 'ReplaySingle' => 'Jedan događaj', + 'ReportEventAudit' => 'Audit Events Report', + 'ResetEventCounts' => 'Resetiraj događaje', + 'Reset' => 'Reset', + 'Restarting' => 'Restartiram', + 'Restart' => 'Restaruj', + 'RestrictedCameraIds' => 'Restricted Camera Ids', + 'RestrictedMonitors' => 'Ograničeni monitori', + 'ReturnDelay' => 'Vrati kašnjenje', + 'ReturnLocation' => 'Vrati lokaciju', + 'Rewind' => 'Premotaj', + 'RotateLeft' => 'Rotoraj ulijevo', + 'RotateRight' => 'Rotiraj udesno', + 'RTSPTransport' => 'RTSP Transport Protocol', + 'RunAudit' => 'Run Audit Process', + 'RunLocalUpdate' => 'Pokrenite zmupdate.pl za ažuriranje', + 'RunMode' => 'Modus rada', + 'Running' => 'Pokrenuto', + 'RunState' => 'Radni modus', + 'RunStats' => 'Pokreni stats proces', + 'RunTrigger' => 'Pokreni triger proces', + 'SaveAs' => 'Spremi kao', + 'SaveFilter' => 'Spremi Filter', + 'SaveJPEGs' => 'Spremi JPEGs', + 'Save' => 'Spremi', + 'Scale' => 'Razmjer', + 'Score' => 'Zbir', + 'Secs' => 'Secs', + 'Sectionlength' => 'Odaberi dužinu', + 'SelectMonitors' => 'SOdaberi monitore', + 'Select' => 'Odaberi', + 'SelectFormat' => 'Odaberi format', + 'SelectLog' => 'Odaberi zapis', + 'SelfIntersecting' => 'Polygon edges must not intersect', + 'SetNewBandwidth' => 'Postavi propusnost na', + 'SetPreset' => 'Postavi pozicije', + 'Set' => 'Postavi', + 'Settings' => 'Postavke', + 'ShowFilterWindow' => 'Prikaži prozor za filter', + 'ShowTimeline' => 'Prikaži vremensku liniju', + 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', + 'Size' => 'Veličina', + 'SkinDescription' => 'Izmjeni izgled za ovu sesiju', + 'CSSDescription' => 'Izmjeni css za ovu sesiju', + 'Sleep' => 'Sleep', + 'SortAsc' => 'Rastuće', + 'SortBy' => 'Sortiraj po', + 'SortDesc' => 'Padajuće', + 'Source' => 'Izvor', + 'SourceColours' => 'Source Colours', + 'SourcePath' => 'Putanja izvora ', + 'SourceType' => 'Izvor videa', + 'SpeedHigh' => 'Velika brzina', + 'SpeedLow' => 'Niska brzina', + 'SpeedMedium' => 'Srednja brzina', + 'Speed' => 'brzina', + 'SpeedTurbo' => 'Turbo brzina', + 'Start' => 'Start', + 'State' => 'Stanje', + 'Stats' => 'Statistka', + 'Status' => 'Status', + 'StatusUnknown' => 'Nepoznato', + 'StatusConnected' => 'Snimam', + 'StatusNotRunning' => 'Nije pokrenuto', + 'StatusRunning' => 'Ne snima', + 'StepBack' => 'Korak nazad', + 'StepForward' => 'Korak naprijed', + 'StepLarge' => 'Veliki korak', + 'StepMedium' => 'Srednji korak', + 'StepNone' => 'Bez koraka', + 'StepSmall' => 'Mali korak', + 'Step' => 'Korak', + 'Stills' => 'Stills', + 'Stopped' => 'Zaustavljeno', + 'Stop' => 'Zaustavi', + 'StorageArea' => 'Storage Area', + 'StorageDoDelete' => 'Brisanja', + 'StorageScheme' => 'Šema', + 'StreamReplayBuffer' => 'Stream Replay Image Buffer', + 'Stream' => 'Stream', + 'Submit' => 'Pošalji', + 'System' => 'Sistem', + 'TargetColorspace' => 'Rezolucija boja', + 'Tele' => 'Udaljeno', + 'Thumbnail' => 'Sličica', + 'Tilt' => 'Tilt', + 'TimeDelta' => 'Vremenska razlika', + 'Timeline' => 'Vremenska linija', + 'TimelineTip1' => 'Pass your mouse over the graph to view a snapshot image and event details.', // Added 2013.08.15. + 'TimelineTip2' => 'Click on the coloured sections of the graph, or the image, to view the event.', // Added 2013.08.15. + 'TimelineTip3' => 'Click on the background to zoom in to a smaller time period based around your click.', // Added 2013.08.15. + 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. + 'TimestampLabelFormat' => 'Timestamp format oznake', + 'TimestampLabelX' => 'Timestamp oznaka X', + 'TimestampLabelY' => 'Timestamp oznaka Y', + 'TimestampLabelSize' => 'Veličina fonta', + 'Timestamp' => 'Timestamp', + 'TimeStamp' => 'Vremenski pečat', + 'Time' => 'Vrijeme', + 'Today' => 'Danas', + 'Tools' => 'Alati', + 'Total' => 'Ukupno', + 'TotalBrScore' => 'Total
Score', + 'TrackDelay' => 'Kašnjenje', + 'TrackMotion' => 'Prati pokret', + 'Triggers' => 'Okidači', + 'TurboPanSpeed' => 'Turbo Pan brzina', + 'TurboTiltSpeed' => 'Turbo Tilt brzina', + 'Type' => 'Tip', + 'Unarchive' => 'Dearhiviraj', + 'Undefined' => 'Nedefinisano', + 'Units' => 'Mjere', + 'Unknown' => 'Nepoznato', + 'UpdateAvailable' => 'Dostupno je novo ažurranje za Zoneminder .', + 'UpdateNotNecessary' => 'Ažuriranje nije potrebno.', + 'Update' => 'Ažuiriaj', + 'Upload' => 'Upload', + 'Updated' => 'Ažurirano', + 'UsedPlugins' => 'Korišteni plugini ', + 'UseFilterExprsPost' => ' filter expressions', // This is used at the end of the phrase 'use N filter expressions' + 'UseFilterExprsPre' => 'Use ', // This is used at the beginning of the phrase 'use N filter expressions' + 'UseFilter' => 'Koristi filter', + 'Username' => 'Korisničko ime', + 'Users' => 'Korisnici', + 'User' => 'Korisnik', + 'Value' => 'Vrijednost', + 'VersionIgnore' => 'Ignoriši ovu verziju', + 'VersionRemindDay' => 'Podsjeti me za jedan dan', + 'VersionRemindHour' => 'Podsjeti me za jedan sat', + 'VersionRemindNever' => 'Ne podsjecaj me na nove verzije', + 'VersionRemindWeek' => 'Podsjeti me za sedam dana', + 'Version' => 'Verzija', + 'VideoFormat' => 'Video Format', + 'VideoGenFailed' => 'Generisanje videa nije uspjelo!', + 'VideoGenFiles' => 'Postojece video datoteke', + 'VideoGenNoFiles' => 'Video datoteke nisu pronadjene', + 'VideoGenParms' => 'Parametri za generisanje videa', + 'VideoGenSucceeded' => 'Generisanje videa uspjelo!', + 'VideoSize' => 'Velicina videa', + 'VideoWriter' => 'Video pisac', + 'Video' => 'Video', + 'ViewAll' => 'Pregledaj sve', + 'ViewEvent' => 'Pregled događaja', + 'ViewPaged' => 'Stanični pregled', + 'View' => 'Pregled', + 'V4L' => 'V4L', + 'V4LCapturesPerFrame' => 'Snimci po frejmu', + 'V4LMultiBuffer' => 'Višestr. bafer', + 'Wake' => 'Budi', + 'WarmupFrames' => 'Warmup frejmovi', + 'Watch' => 'Gledaj', + 'WebColour' => 'Web boja', + 'Web' => 'Web', + 'WebSiteUrl' => 'URL web stranice', + 'Week' => 'Sedmica', + 'WhiteBalance' => 'Balans bijele', + 'White' => 'Bijelo', + 'Wide' => 'Široko', + 'X10ActivationString' => 'X10 znakovni niz za aktiviranje', + 'X10InputAlarmString' => 'X10 ulazni znakovni niz za alarm', + 'X10OutputAlarmString' => 'X10 izlazni znakovni niz za alarm', + 'X10' => 'X10', + 'X' => 'X', + 'Yes' => 'Da', + 'YouNoPerms' => 'Nemate potrebne dozvole za pristup ovom resursu.', + 'Y' => 'Y', + 'ZoneAlarmColour' => 'Boja alarma (Red/Green/Blue)', + 'ZoneArea' => 'Oblast zone', + 'ZoneFilterSize' => 'Filter Width/Height (pixels)', + 'ZoneMinderLog' => 'ZoneMinder zapisnik', + 'ZoneMinMaxAlarmArea' => 'Min/Max alarmirana oblast', + 'ZoneMinMaxBlobArea' => 'Min/Max blob oblast', + 'ZoneMinMaxBlobs' => 'Min/Max Blobovi', + 'ZoneMinMaxFiltArea' => 'Min/Max filtrirane oblasti', + 'ZoneMinMaxPixelThres' => 'Min/Max Pixel Threshold (0-255)', + 'ZoneOverloadFrames' => 'Overload Frame Ignore Count', + 'ZoneExtendAlarmFrames' => 'Extend Alarm Frame Count', + 'Zones' => 'Zone', + 'Zone' => 'Zona', + 'ZoomIn' => 'Zoom In', + 'ZoomOut' => 'Zoom Out', + 'Zoom' => 'Zumiranje', +); + +// Complex replacements with formatting and/or placements, must be passed through sprintf +$CLANG = array( + 'CurrentLogin' => 'Prijavljeni ste kao \'%1$s\'', + 'EventCount' => '%1$s %2$s', // For example '37 Events' (from Vlang below) + 'LastEvents' => 'Last %1$s %2$s', // For example 'Last 37 Events' (from Vlang below) + 'LatestRelease' => 'Zadnja verzija servera je v%1$s, vi imate v%2$s.', + 'MonitorCount' => '%1$s %2$s', // For example '4 Monitors' (from Vlang below) + 'MonitorFunction' => 'Monitor %1$s Function', + 'RunningRecentVer' => 'Koristite najnoviju verziju Zoneminder servera, v%s.', + 'VersionMismatch' => 'Version mismatch, system is version %1$s, database is %2$s.', +); + +// The next section allows you to describe a series of word ending and counts used to +// generate the correctly conjugated forms of words depending on a count that is associated +// with that word. +// This intended to allow phrases such a '0 potatoes', '1 potato', '2 potatoes' etc to +// conjugate correctly with the associated count. +// In some languages such as English this is fairly simple and can be expressed by assigning +// a count with a singular or plural form of a word and then finding the nearest (lower) value. +// So '0' of something generally ends in 's', 1 of something is singular and has no extra +// ending and 2 or more is a plural and ends in 's' also. So to find the ending for '187' of +// something you would find the nearest lower count (2) and use that ending. +// +// So examples of this would be +// $zmVlangPotato = array( 0=>'Potatoes', 1=>'Potato', 2=>'Potatoes' ); +// $zmVlangSheep = array( 0=>'Sheep' ); +// +// where you can have as few or as many entries in the array as necessary +// If your language is similar in form to this then use the same format and choose the +// appropriate zmVlang function below. +// If however you have a language with a different format of plural endings then another +// approach is required . For instance in Russian the word endings change continuously +// depending on the last digit (or digits) of the numerator. In this case then zmVlang +// arrays could be written so that the array index just represents an arbitrary 'type' +// and the zmVlang function does the calculation about which version is appropriate. +// +// So an example in Russian might be (using English words, and made up endings as I +// don't know any Russian!!) +// 'Potato' => array( 1=>'Potati', 2=>'Potaton', 3=>'Potaten' ), +// +// and the zmVlang function decides that the first form is used for counts ending in +// 0, 5-9 or 11-19 and the second form when ending in 1 etc. +// + +// Variable arrays expressing plurality, see the zmVlang description above +$VLANG = array( + 'Event' => array( 0=>'Events', 1=>'Event', 2=>'Events' ), + 'Monitor' => array( 0=>'Monitors', 1=>'Monitor', 2=>'Monitors' ), +); +// You will need to choose or write a function that can correlate the plurality string arrays +// with variable counts. This is used to conjugate the Vlang arrays above with a number passed +// in to generate the correct noun form. +// +// In languages such as English this is fairly simple +// Note this still has to be used with printf etc to get the right formatting +function zmVlang( $langVarArray, $count ) +{ + krsort( $langVarArray ); + foreach ( $langVarArray as $key=>$value ) + { + if ( abs($count) >= $key ) + { + return( $value ); + } + } + die( 'Error, unable to correlate variable language string' ); +} + +// This is an version that could be used in the Russian example above +// The rules are that the first word form is used if the count ends in +// 0, 5-9 or 11-19. The second form is used then the count ends in 1 +// (not including 11 as above) and the third form is used when the +// count ends in 2-4, again excluding any values ending in 12-14. +// +// function zmVlang( $langVarArray, $count ) +// { +// $secondlastdigit = substr( $count, -2, 1 ); +// $lastdigit = substr( $count, -1, 1 ); +// // or +// // $secondlastdigit = ($count/10)%10; +// // $lastdigit = $count%10; +// +// // Get rid of the special cases first, the teens +// if ( $secondlastdigit == 1 && $lastdigit != 0 ) +// { +// return( $langVarArray[1] ); +// } +// switch ( $lastdigit ) +// { +// case 0 : +// case 5 : +// case 6 : +// case 7 : +// case 8 : +// case 9 : +// { +// return( $langVarArray[1] ); +// break; +// } +// case 1 : +// { +// return( $langVarArray[2] ); +// break; +// } +// case 2 : +// case 3 : +// case 4 : +// { +// return( $langVarArray[3] ); +// break; +// } +// } +// die( 'Error, unable to correlate variable language string' ); +// } + +// This is an example of how the function is used in the code which you can uncomment and +// use to test your custom function. +//$monitors = array(); +//$monitors[] = 1; // Choose any number +//echo sprintf( $CLANG['MonitorCount'], count($monitors), zmVlang( $VLANG['VlangMonitor'], count($monitors) ) ); + +// In this section you can override the default prompt and help texts for the options area +// These overrides are in the form show below where the array key represents the option name minus the initial ZM_ +// So for example, to override the help text for ZM_LANG_DEFAULT do +$OLANG = array( + 'OPTIONS_FFMPEG' => array( + 'Help' => "Parameters in this field are passed on to FFmpeg. Multiple parameters can be separated by ,~~ ". + "Examples (do not enter quotes)~~~~". + "\"allowed_media_types=video\" Set datatype to request fromcam (audio, video, data)~~~~". + "\"reorder_queue_size=nnn\" Set number of packets to buffer for handling of reordered packets~~~~". + "\"loglevel=debug\" Set verbosity of FFmpeg (quiet, panic, fatal, error, warning, info, verbose, debug)" + ), + 'OPTIONS_RTSPTrans' => array( + 'Help' => "This sets the RTSP Transport Protocol for FFmpeg.~~ ". + "TCP - Use TCP (interleaving within the RTSP control channel) as transport protocol.~~". + "UDP - Use UDP as transport protocol. Higher resolution cameras have experienced some 'smearing' while using UDP, if so try TCP~~". + "UDP Multicast - Use UDP Multicast as transport protocol~~". + "HTTP - Use HTTP tunneling as transport protocol, which is useful for passing proxies.~~" + ), + 'OPTIONS_LIBVLC' => array( + 'Help' => "Parameters in this field are passed on to libVLC. Multiple parameters can be separated by ,~~ ". + "Examples (do not enter quotes)~~~~". + "\"--rtp-client-port=nnn\" Set local port to use for rtp data~~~~". + "\"--verbose=2\" Set verbosity of libVLC" + ), + 'OPTIONS_EXIF' => array( + 'Help' => "Enable this option to embed EXIF data into each jpeg frame." + ), + 'OPTIONS_RTSPDESCRIBE' => array( + 'Help' => "Sometimes, during the initial RTSP handshake, the camera will send an updated media URL. ". + "Enable this option to tell ZoneMinder to use this URL. Disable this option to ignore the ". + "value from the camera and use the value as entered in the monitor configuration~~~~". + "Generally this should be enabled. However, there are cases where the camera can get its". + "own URL incorrect, such as when the camera is streaming through a firewall"), + 'OPTIONS_MAXFPS' => array( + 'Help' => "This field has certain limitations when used for non-local devices.~~ ". + "Failure to adhere to these limitations will cause a delay in live video, irregular frame skipping, ". + "and missed events~~". + "For streaming IP cameras, do not use this field to reduce the frame rate. Set the frame rate in the". + " camera, instead. You can, however, use a value that is slightly higher than the frame rate in the camera. ". + "In this case, this helps keep the cpu from being overtaxed in the event of a network problem.~~". + "Some, mostly older, IP cameras support snapshot mode. In this case ZoneMinder is actively polling the camera ". + "for new images. In this case, it is safe to use the field." + ), + +// 'LANG_DEFAULT' => array( +// 'Prompt' => "This is a new prompt for this option", +// 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked" +// ), +); + +?> diff --git a/web/lang/big5_big5.php b/web/lang/big5_big5.php index b8bc173a5..922140698 100644 --- a/web/lang/big5_big5.php +++ b/web/lang/big5_big5.php @@ -82,6 +82,8 @@ $SLANG = array( 'Actual' => 'Actual', 'AddNewControl' => '新增控制', 'AddNewMonitor' => '新增監視', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => '新增使用者', 'AddNewZone' => '新增監視區', 'Alarm' => '警報', @@ -109,22 +111,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Archive Status', 'AttrAvgScore' => 'Average Score', 'AttrCause' => 'Cause', - 'AttrDate' => 'Date', - 'AttrDateTime' => 'Date/Time', 'AttrDiskBlocks' => 'Disk Blocks', 'AttrDiskPercent' => 'Disk Percent', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Duration', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Frames', 'AttrId' => 'Id', 'AttrMaxScore' => 'Max. Score', 'AttrMonitorId' => 'Monitor Id', 'AttrMonitorName' => 'Monitor Name', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Name', 'AttrNotes' => 'Notes', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'System Load', - 'AttrTime' => 'Time', 'AttrTotalScore' => 'Total Score', - 'AttrWeekday' => 'Weekday', 'Auto' => '自動', 'AutoStopTimeout' => '時間過自動停止', 'Available' => 'Available', // Added - 2009-03-31 @@ -157,9 +169,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer', 'BadSectionLength' => 'Section length must be an integer of 30 or more', 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Stream replay buffer must be an integer of zero or more', 'BadWarmupCount' => 'Warmup frames must be an integer of zero or more', 'BadWebColour' => 'Web colour must be a valid web colour string', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Width must be set to a valid value', 'Bandwidth' => '頻寬', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -195,6 +209,7 @@ $SLANG = array( 'CanMoveRel' => 'Can Move Relative', 'CanPan' => 'Can Pan' , 'CanReset' => 'Can Reset', + 'CanReboot' => 'Can Reboot', 'CanSetPresets' => 'Can Set Presets', 'CanSleep' => 'Can Sleep', 'CanTilt' => 'Can Tilt', @@ -223,10 +238,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Choose Preset', 'Clear' => 'Clear', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => '關閉', 'Colour' => 'Colour', 'Command' => 'Command', 'Component' => 'Component', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Config', 'ConfiguredFor' => '配置為', 'ConfirmDeleteEvents' => 'Are you sure you wish to delete the selected events?', @@ -276,7 +293,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'Please Donate', 'DonateAlready' => 'No, I\'ve already donated', - 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', + 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', 'DonateRemindDay' => 'Not yet, remind again in 1 day', 'DonateRemindHour' => 'Not yet, remind again in 1 hour', 'DonateRemindMonth' => 'Not yet, remind again in 1 month', @@ -284,9 +301,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Not yet, remind again in 1 week', 'DonateYes' => 'Yes, I\'d like to donate now', 'Download' => '下載', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 'Duration' => '歷時', 'Edit' => '編輯', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => '啟動警報', 'Enabled' => '啟用', @@ -303,6 +322,7 @@ $SLANG = array( 'Events' => '事件', 'Exclude' => '不包含', 'Execute' => 'Execute', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => '輸出', 'ExportDetails' => '輸出事件細項', 'ExportFailed' => '輸出失敗', @@ -332,8 +352,10 @@ $SLANG = array( 'FilterExecuteEvents' => '自動執行符合指令', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => '自動發出符合訊息', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filter Px', 'FilterUnset' => '您必需設定濾鏡的寬度和高度', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => '自動上傳符合項目', 'FilterVideoEvents' => '自動產生符合的影像檔', 'Filters' => '濾鏡', @@ -358,6 +380,7 @@ $SLANG = array( 'Function' => '功能', 'Gain' => 'Gain', 'General' => '一般', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => '輸出影片', 'GeneratingVideo' => '輸出影片中', 'GoToZoneMinder' => 'Go to ZoneMinder.com', @@ -378,6 +401,7 @@ $SLANG = array( 'High' => '高', 'HighBW' => 'High B/W', 'Home' => 'Home', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => '時', 'Hue' => 'Hue', 'Id' => 'Id', @@ -402,6 +426,7 @@ $SLANG = array( 'Line' => 'Line', // Added - 2011-06-16 'LinkedMonitors' => 'Linked Monitors', 'List' => '列出', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => '載入', 'Local' => 'Local', 'Log' => 'Log', // Added - 2011-06-16 @@ -488,6 +513,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 'Monitors' => '監視', 'Montage' => '全部顯示', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => '月', 'More' => 'More', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -514,6 +540,7 @@ $SLANG = array( 'Next' => '下一步', 'No' => 'No', 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'There are no frames recorded for this event', 'NoGroup' => 'No Group', // Added - 2009-02-08 'NoSavedFilters' => 'NoSavedFilters', @@ -532,6 +559,8 @@ $SLANG = array( 'OpGt' => 'greater than', 'OpGtEq' => 'greater than or equal to', 'OpIn' => 'in set', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'less than', 'OpLtEq' => 'less than or equal to', 'OpMatches' => 'matches', @@ -541,6 +570,7 @@ $SLANG = array( 'Open' => 'Open', 'OptionHelp' => 'OptionHelp', 'OptionRestartWarning' => 'These changes may not come into effect fully\nwhile the system is running. When you have\nfinished making your changes please ensure that\nyou restart ZoneMinder.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => '銓垣專用',//進階選項 'OrEnterNewName' => 'or enter new name', 'Order' => '順序', @@ -578,9 +608,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Rate', 'Real' => 'Real', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => '錄影', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => '參考影像混合 %ge', 'Refresh' => '更新', 'Remote' => 'Remote', @@ -596,6 +630,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Reset', 'ResetEventCounts' => 'Reset Event Counts', 'Restart' => '重新啟動', @@ -614,6 +649,7 @@ $SLANG = array( 'Save' => '存檔', 'SaveAs' => '儲存為', 'SaveFilter' => 'Save Filter', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Scale', 'Score' => '分數', 'Secs' => 'Secs', @@ -630,6 +666,7 @@ $SLANG = array( 'ShowFilterWindow' => '顯示過濾視窗', 'ShowTimeline' => 'Show Timeline', 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Size', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 'Sleep' => 'Sleep', @@ -649,6 +686,10 @@ $SLANG = array( 'State' => 'State', 'Stats' => 'Stats', 'Status' => 'Status', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Step', 'StepBack' => 'Step Back', 'StepForward' => 'Step Forward', @@ -659,6 +700,8 @@ $SLANG = array( 'Stills' => '靜止', 'Stop' => '停止', 'Stopped' => '已停止', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => '串流', 'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Submit' => 'Submit', @@ -678,6 +721,7 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => '時間格式', 'TimestampLabelFormat' => '時間標示格式', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => '時間標示 X', 'TimestampLabelY' => '時間標示 Y', 'Today' => 'Today', @@ -724,6 +768,7 @@ $SLANG = array( 'VideoGenParms' => '輸出影片參數', 'VideoGenSucceeded' => 'Video Generation Succeeded!', // Added - 2009-02-08 'VideoSize' => '影片尺寸', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => '檢視', 'ViewAll' => '全部檢視', 'ViewEvent' => 'View Event', // Added - 2009-02-08 @@ -733,6 +778,7 @@ $SLANG = array( 'Watch' => 'Watch', 'Web' => 'Web', 'WebColour' => 'Web Colour', // Added - 2009-02-08 + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => '週', 'White' => 'White', 'WhiteBalance' => 'White Balance', diff --git a/web/lang/cn_zh.php b/web/lang/cn_zh.php index 9ed821417..0a751023e 100644 --- a/web/lang/cn_zh.php +++ b/web/lang/cn_zh.php @@ -78,6 +78,8 @@ $SLANG = array( 'Actual' => '实际', 'AddNewControl' => '新建控制', 'AddNewMonitor' => '新建监视器', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => '新建用户', 'AddNewZone' => '新建区域', 'Alarm' => '报警', @@ -105,22 +107,32 @@ $SLANG = array( 'AttrArchiveStatus' => '存档状态', 'AttrAvgScore' => '平均分数', 'AttrCause' => '原因', - 'AttrDate' => '日期', - 'AttrDateTime' => '日期/时间', 'AttrDiskBlocks' => '磁碟区块', 'AttrDiskPercent' => '磁碟百分比', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => '过程', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => '帧', 'AttrId' => 'Id', 'AttrMaxScore' => '最大分数', 'AttrMonitorId' => '监视器 Id', 'AttrMonitorName' => '监视器名称', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => '名称', 'AttrNotes' => '备注', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => '系统负载', - 'AttrTime' => '时间', 'AttrTotalScore' => '总分数', - 'AttrWeekday' => '星期', 'Auto' => '自动', 'AutoStopTimeout' => '超时自动停止', 'Available' => 'Available', // Added - 2009-03-31 @@ -153,9 +165,11 @@ $SLANG = array( 'BadRefBlendPerc' => '参考混合百分比必须设为一个正整数', 'BadSectionLength' => '节长度必须设为30的整数倍', 'BadSignalCheckColour' => '信号检查颜色必须设为有效的RGB颜色字符', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer' => '流重放缓冲必须为零或更多整数', 'BadWarmupCount' => '预热帪必须设为零或更多整数', 'BadWebColour' => 'Web颜色必须设为有效Web颜色字符', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => '宽度必须设为有效值', 'Bandwidth' => '带宽', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -191,6 +205,7 @@ $SLANG = array( 'CanMoveRel' => '可以相对移动', 'CanPan' => '可以平移' , 'CanReset' => '可以复位', + 'CanReboot' => 'Can Reboot', 'CanSetPresets' => '可以进行预设', 'CanSleep' => '可以休眠', 'CanTilt' => '可以倾斜', @@ -219,10 +234,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => '选择预置', 'Clear' => 'Clear', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => '关闭', 'Colour' => '彩色', 'Command' => '命令', 'Component' => 'Component', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => '配置', 'ConfiguredFor' => '配置标的', 'ConfirmDeleteEvents' => '确认希望删除所选事件?', @@ -272,7 +289,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => '请捐款', 'DonateAlready' => '不,我已经捐赠过了', - 'DonateEnticement' => '迄今,您已经运行ZoneMinder有一阵子了,希望它能够有助于增强您家或者办公区域的安全。尽管ZoneMinder是,并将保持免费和开源,该项目依然在研发和支持中投入了资金和精力。如果您愿意支持今后的开发和新功能,那么请考虑为该项目捐款。捐款不是必须的,任何数量的捐赠,我们都很感谢。

如果您愿意捐款,请选择下列选项,或者访问 http://www.zoneminder.com/donate.html 捐赠主页。

感谢您使用ZoneMinder,并且不要忘记访问访问ZoneMinder.com的论坛以获得支持或建议,这可以提升您的ZoneMinder的体验。', + 'DonateEnticement' => '迄今,您已经运行ZoneMinder有一阵子了,希望它能够有助于增强您家或者办公区域的安全。尽管ZoneMinder是,并将保持免费和开源,该项目依然在研发和支持中投入了资金和精力。如果您愿意支持今后的开发和新功能,那么请考虑为该项目捐款。捐款不是必须的,任何数量的捐赠,我们都很感谢。

如果您愿意捐款,请选择下列选项,或者访问 https://zoneminder.com/donate/ 捐赠主页。

感谢您使用ZoneMinder,并且不要忘记访问访问ZoneMinder.com的论坛以获得支持或建议,这可以提升您的ZoneMinder的体验。', 'DonateRemindDay' => '现在不,1天内再次提醒我', 'DonateRemindHour' => '现在不,1小时内再次提醒我', 'DonateRemindMonth' => '现在不,1个月内再次提醒我', @@ -280,9 +297,11 @@ $SLANG = array( 'DonateRemindWeek' => '现在不,1星期内再次提醒我', 'DonateYes' => '好,我现在就捐款', 'Download' => '下载', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 'Duration' => 'Duration', 'Edit' => '编辑', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => '启动报警', 'Enabled' => '已启动', @@ -299,6 +318,7 @@ $SLANG = array( 'Events' => '事件', 'Exclude' => '排除', 'Execute' => '执行', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => '导出', 'ExportDetails' => '导出时间详情', 'ExportFailed' => '导出失败', @@ -328,8 +348,10 @@ $SLANG = array( 'FilterExecuteEvents' => '执行全部匹配项命令', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => '全部匹配项的信息详情', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => '过滤器像素', 'FilterUnset' => '您必须指定过滤器宽度和高度', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => '上传全部匹配项', 'FilterVideoEvents' => '为全部匹配项创建视频', 'Filters' => '过滤器', @@ -354,6 +376,7 @@ $SLANG = array( 'Function' => '功能', 'Gain' => '增益', 'General' => '一般', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => '创建视频', 'GeneratingVideo' => '正在创建视频', 'GoToZoneMinder' => '访问 ZoneMinder.com', @@ -374,6 +397,7 @@ $SLANG = array( 'High' => '高', 'HighBW' => '高 B/W', 'Home' => '主页', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => '小时', 'Hue' => '色调', 'Id' => 'Id', @@ -398,6 +422,7 @@ $SLANG = array( 'Line' => 'Line', // Added - 2011-06-16 'LinkedMonitors' => '管理监视器', 'List' => '列表', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => '加载', 'Local' => '本地', 'Log' => 'Log', // Added - 2011-06-16 @@ -484,6 +509,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 'Monitors' => '监视器', 'Montage' => '镜头组接', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => '月', 'More' => 'More', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -510,6 +536,7 @@ $SLANG = array( 'Next' => '下一个', 'No' => '不', 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => '该事件没有相关帧的记录', 'NoGroup' => '无组', 'NoSavedFilters' => '没有保存过滤器', @@ -528,6 +555,8 @@ $SLANG = array( 'OpGt' => '大于', 'OpGtEq' => '大于等于', 'OpIn' => '在集', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => '小于', 'OpLtEq' => '小于等于', 'OpMatches' => '匹配', @@ -537,6 +566,7 @@ $SLANG = array( 'Open' => '打开', 'OptionHelp' => '选项帮助', 'OptionRestartWarning' => '这些改动在系统运行时可以不会完全生效.\n 当你设置完毕改动后\n请确认\n您重新启动 ZoneMinder.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => '选项', 'OrEnterNewName' => '或输入新名词', 'Order' => '次序', @@ -574,9 +604,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => '协议', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => '速率', 'Real' => '实际', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => '记录', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => '参考影像混合 %ge', 'Refresh' => '刷新', 'Remote' => '远程', @@ -592,6 +626,7 @@ $SLANG = array( 'ReplayAll' => '全部事件', 'ReplayGapless' => '无间隙事件', 'ReplaySingle' => '单一事件', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => '重置', 'ResetEventCounts' => '重置事件数', 'Restart' => '重启动', @@ -610,6 +645,7 @@ $SLANG = array( 'Save' => '保存', 'SaveAs' => '另存为', 'SaveFilter' => '存储过滤器', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => '比例', 'Score' => '分数', 'Secs' => '秒', @@ -626,6 +662,7 @@ $SLANG = array( 'ShowFilterWindow' => '显示过滤器视窗', 'ShowTimeline' => '显示时间轴', 'SignalCheckColour' => '型号检查颜色', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => '大小', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 'Sleep' => '睡眠', @@ -645,6 +682,10 @@ $SLANG = array( 'State' => '状态', 'Stats' => '统计', 'Status' => '状况', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => '步进', 'StepBack' => '单步后退', 'StepForward' => '单步前进', @@ -655,6 +696,8 @@ $SLANG = array( 'Stills' => '静止', 'Stop' => '停止', 'Stopped' => '已停止', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => '流', 'StreamReplayBuffer' => '流重放影像缓冲', 'Submit' => '发送', @@ -674,6 +717,7 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => '时间戳', 'TimestampLabelFormat' => '时间戳标签格式', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => '时间戳标签 X', 'TimestampLabelY' => '时间戳标签 Y', 'Today' => '今天', @@ -720,6 +764,7 @@ $SLANG = array( 'VideoGenParms' => '视频产生参数', 'VideoGenSucceeded' => '视频产生成功!', 'VideoSize' => '视频尺寸', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => '查看', 'ViewAll' => '查看全部', 'ViewEvent' => '查看事件', @@ -729,6 +774,7 @@ $SLANG = array( 'Watch' => '观察', 'Web' => 'Web', 'WebColour' => 'Web颜色', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => '周', 'White' => '白', 'WhiteBalance' => '白平衡', diff --git a/web/lang/cs_cz.php b/web/lang/cs_cz.php index adcdeb61d..c6340ae76 100644 --- a/web/lang/cs_cz.php +++ b/web/lang/cs_cz.php @@ -78,6 +78,8 @@ $SLANG = array( 'Actual' => 'Skutečná', 'AddNewControl' => 'Přidat nové řízení', 'AddNewMonitor' => 'Přidat kameru', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Přidat uživatele', 'AddNewZone' => 'Přidat zónu', 'Alarm' => 'Alarm', @@ -105,22 +107,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Archiv status', 'AttrAvgScore' => 'Prům. skóre', 'AttrCause' => 'Příčina', - 'AttrDate' => 'Datum', - 'AttrDateTime' => 'Datum/Čas', 'AttrDiskBlocks' => 'Bloky disku', 'AttrDiskPercent' => 'Zaplnění disku', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Průběh', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Snímky', 'AttrId' => 'Id', 'AttrMaxScore' => 'Max. skóre', 'AttrMonitorId' => 'Kamera Id', 'AttrMonitorName' => 'Jméno kamery', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Jméno', 'AttrNotes' => 'Notes', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'System Load', - 'AttrTime' => 'Čas', 'AttrTotalScore' => 'Celkové skóre', - 'AttrWeekday' => 'Den v týdnu', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Časový limit pro vypršení', 'Available' => 'Available', // Added - 2009-03-31 @@ -153,9 +165,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer', 'BadSectionLength' => 'Section length must be an integer of 30 or more', 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Stream replay buffer must be an integer of zero or more', 'BadWarmupCount' => 'Warmup frames must be an integer of zero or more', 'BadWebColour' => 'Web colour must be a valid web colour string', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Width must be set to a valid value', 'Bandwidth' => 'Rychlost sítě', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -191,6 +205,7 @@ $SLANG = array( 'CanMoveRel' => 'Umí relativní pohyb', 'CanPan' => 'Umí otáčení', 'CanReset' => 'Umí reset', + 'CanReboot' => 'Can Reboot', 'CanSetPresets' => 'Umí navolit předvolby', 'CanSleep' => 'Může spát', 'CanTilt' => 'Umí náklon', @@ -219,10 +234,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Choose Preset', 'Clear' => 'Clear', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Zavřít', 'Colour' => 'Barva', 'Command' => 'Příkaz', 'Component' => 'Component', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Nastavení', 'ConfiguredFor' => 'Nastaveno pro', 'ConfirmDeleteEvents' => 'Are you sure you wish to delete the selected events?', @@ -272,7 +289,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'Prosím podpořte', 'DonateAlready' => 'Ne, už jsem podpořil', - 'DonateEnticement' => 'Již nějakou dobu používáte software ZoneMinder k ochraně svého majetku a předpokládám, že jej shledáváte užitečným. Přestože je ZoneMinder, znovu připomínám, zdarma a volně šířený software, stojí jeho vývoj a podpora nějaké peníze. Pokud byste chtěl/a podpořit budoucí vývoj a nové možnosti softwaru, prosím zvažte darování finanční pomoci. Darování je, samozřejmě, dobrovolné, ale zato velmi ceněné můžete přispět jakou částkou chcete.

Pokud máte zájem podpořit náš tým, prosím, vyberte níže uvedenou možnost, nebo navštivte http://www.zoneminder.com/donate.html.

Děkuji Vám že jste si vybral/a software ZoneMinder a nezapomeňte navštívit fórum na ZoneMinder.com pro podporu a návrhy jak udělat ZoneMinder ještě lepším než je dnes.', + 'DonateEnticement' => 'Již nějakou dobu používáte software ZoneMinder k ochraně svého majetku a předpokládám, že jej shledáváte užitečným. Přestože je ZoneMinder, znovu připomínám, zdarma a volně šířený software, stojí jeho vývoj a podpora nějaké peníze. Pokud byste chtěl/a podpořit budoucí vývoj a nové možnosti softwaru, prosím zvažte darování finanční pomoci. Darování je, samozřejmě, dobrovolné, ale zato velmi ceněné můžete přispět jakou částkou chcete.

Pokud máte zájem podpořit náš tým, prosím, vyberte níže uvedenou možnost, nebo navštivte https://zoneminder.com/donate/.

Děkuji Vám že jste si vybral/a software ZoneMinder a nezapomeňte navštívit fórum na ZoneMinder.com pro podporu a návrhy jak udělat ZoneMinder ještě lepším než je dnes.', 'DonateRemindDay' => 'Nyní ne, připomenout za 1 den', 'DonateRemindHour' => 'Nyní ne, připomenout za hodinu', 'DonateRemindMonth' => 'Nyní ne, připomenout za měsíc', @@ -280,9 +297,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Nyní ne, připomenout za týden', 'DonateYes' => 'Ano, chcit podpořit ZoneMinder nyní', 'Download' => 'Stáhnout', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 'Duration' => 'Průběh', 'Edit' => 'Editovat', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Povolit alarmy', 'Enabled' => 'Povoleno', @@ -299,6 +318,7 @@ $SLANG = array( 'Events' => 'Záznamy', 'Exclude' => 'Vyjmout', 'Execute' => 'Execute', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Exportovat', 'ExportDetails' => 'Exportovat detaily záznamu', 'ExportFailed' => 'Chyba při exportu', @@ -328,8 +348,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Spustit příkaz na všech nalezených', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Podat zprávu o všech nalezených', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filtr Px', 'FilterUnset' => 'You must specify a filter width and height', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Uploadovat nalezené', 'FilterVideoEvents' => 'Create video for all matches', 'Filters' => 'Filtry', @@ -354,6 +376,7 @@ $SLANG = array( 'Function' => 'Funkce', 'Gain' => 'Zisk', 'General' => 'General', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Generovat video', 'GeneratingVideo' => 'Generuji video', 'GoToZoneMinder' => 'Jít na ZoneMinder.com', @@ -374,6 +397,7 @@ $SLANG = array( 'High' => 'Rychlá', 'HighBW' => 'Rychlá B/W', 'Home' => 'Domů', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Hodina', 'Hue' => 'Odstín', 'Id' => 'Id', @@ -398,6 +422,7 @@ $SLANG = array( 'Line' => 'Line', // Added - 2011-06-16 'LinkedMonitors' => 'Linked Monitors', 'List' => 'Seznam', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Load', 'Local' => 'Lokální', 'Log' => 'Log', // Added - 2011-06-16 @@ -484,6 +509,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 'Monitors' => 'Kamery', 'Montage' => 'Sestřih', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Měsíc', 'More' => 'More', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -510,6 +536,7 @@ $SLANG = array( 'Next' => 'Další', 'No' => 'Ne', 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Pro tento snímek nejsou žádné záznamy', 'NoGroup' => 'No Group', 'NoSavedFilters' => 'Žádné uložené filtry', @@ -528,6 +555,8 @@ $SLANG = array( 'OpGt' => 'větší', 'OpGtEq' => 'větší nebo rovno', 'OpIn' => 'nin set', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'menší', 'OpLtEq' => 'menší nebo rovno', 'OpMatches' => 'obsahuje', @@ -537,6 +566,7 @@ $SLANG = array( 'Open' => 'Otevřít', 'OptionHelp' => 'MožnostHelp', 'OptionRestartWarning' => 'Tyto změny se neprojeví\ndokud systém běží. Jakmile\ndokončíte provádění změn prosím\nrestartujte ZoneMinder.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Možnosti', 'OrEnterNewName' => 'nebo vložte nové jméno', 'Order' => 'Pořadí', @@ -574,9 +604,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Rychlost', 'Real' => 'Skutečná', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Nahrávat', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Reference Image Blend %ge', 'Refresh' => 'Obnovit', 'Remote' => 'Síťová', @@ -592,6 +626,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Reset', 'ResetEventCounts' => 'Resetovat počty záznamů', 'Restart' => 'Restartovat', @@ -610,6 +645,7 @@ $SLANG = array( 'Save' => 'Uložit', 'SaveAs' => 'Uložit jako', 'SaveFilter' => 'Uložit filtr', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Velikost', 'Score' => 'Skóre', 'Secs' => 'Délka(s)', @@ -626,6 +662,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Zobrazit filtr', 'ShowTimeline' => 'Zobrazit časovou linii ', 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Velikost', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 'Sleep' => 'Spát', @@ -645,6 +682,10 @@ $SLANG = array( 'State' => 'Stav', 'Stats' => 'Statistiky', 'Status' => 'Status', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Krok', 'StepBack' => 'Step Back', 'StepForward' => 'Step Forward', @@ -655,6 +696,8 @@ $SLANG = array( 'Stills' => 'Snímky', 'Stop' => 'Zastavit', 'Stopped' => 'Zastaven', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Stream', 'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Submit' => 'Potvrdit', @@ -674,6 +717,7 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => 'Razítko', 'TimestampLabelFormat' => 'Formát časového razítka', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'Časové razítko X', 'TimestampLabelY' => 'Časové razítko Y', 'Today' => 'Dnes', @@ -720,6 +764,7 @@ $SLANG = array( 'VideoGenParms' => 'Parametry generování videa', 'VideoGenSucceeded' => 'Video vygenerováno úspěšně!', 'VideoSize' => 'Velikost videa', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Zobrazit', 'ViewAll' => 'Zobrazit všechny', 'ViewEvent' => 'Zobrazit záznam', @@ -729,6 +774,7 @@ $SLANG = array( 'Watch' => 'Sledovat', 'Web' => 'Web', 'WebColour' => 'Webová barva', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Týden', 'White' => 'Bílá', 'WhiteBalance' => 'Vyvážení bílé', diff --git a/web/lang/de_de.php b/web/lang/de_de.php index 424d91e94..e8bb8218a 100644 --- a/web/lang/de_de.php +++ b/web/lang/de_de.php @@ -80,6 +80,8 @@ $SLANG = array( 'Actual' => 'Original', 'AddNewControl' => 'Neues Steuerelement hinzufügen', 'AddNewMonitor' => 'Neuer Monitor', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Neuer Benutzer', 'AddNewZone' => 'Neue Zone', 'Alarm' => 'Alarm', @@ -107,22 +109,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Archivstatus', 'AttrAvgScore' => 'Mittlere Wertung', 'AttrCause' => 'Grund', - 'AttrDate' => 'Datum', - 'AttrDateTime' => 'Datum/Zeit', 'AttrDiskBlocks' => 'Disk-Blöcke', 'AttrDiskPercent' => 'Disk-Prozent', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Dauer', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Bilder', 'AttrId' => 'ID', 'AttrMaxScore' => 'Maximale Wertung', 'AttrMonitorId' => 'Monitor-ID', 'AttrMonitorName' => 'Monitorname', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Name', 'AttrNotes' => 'Bemerkungen', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'Systemlast', - 'AttrTime' => 'Zeit', 'AttrTotalScore' => 'Gesamtwertung', - 'AttrWeekday' => 'Wochentag', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Auto-Stopp-Zeitüberschreitung', 'Available' => 'Verfügbar', // Added - 2009-03-31 @@ -155,9 +167,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Der Referenz-Blenden-Prozentwert muss ganzzahlig 0 oder größer sein', 'BadSectionLength' => 'Die Bereichslänge muss ganzzahlig 0 oder größer sein', 'BadSignalCheckColour' => 'Die Signalprüffarbe muss auf einen gültigen Farbwert eingestellt sein', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Der Wiedergabestrompuffer muss eine ganze Zahl von null oder mehr betragen', 'BadWarmupCount' => 'Die Anzahl der Vorwärmbilder muss ganzzahlig 0 oder größer sein', 'BadWebColour' => 'Die Webfarbe muss auf einen gültigen Farbwert eingestellt sein', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Die Breite muss auf einen gültigen Wert eingestellt sein', 'Bandwidth' => 'Bandbreite', 'BandwidthHead' => 'Bandbreite', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -193,6 +207,7 @@ $SLANG = array( 'CanMoveRel' => 'Kann relative Bewegung', 'CanPan' => 'Kann Pan' , 'CanReset' => 'Kann Reset', + 'CanReboot' => 'Can Reboot', 'CanSetPresets' => 'Kann Voreinstellungen setzen', 'CanSleep' => 'Kann Sleep', 'CanTilt' => 'Kann Neigung', @@ -221,10 +236,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Log-Auswahl', // Added - 2011-06-17 'ChoosePreset' => 'Voreinstellung auswählen', 'Clear' => 'Leeren', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Schließen', 'Colour' => 'Farbe', 'Command' => 'Kommando', 'Component' => 'Komponente', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Konfig.', 'ConfiguredFor' => 'Konfiguriert für', 'ConfirmDeleteEvents' => 'Sind Sie sicher, dass Sie die ausgewählten Ereignisse löschen wollen?', @@ -274,7 +291,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'Bitte spenden Sie.', 'DonateAlready' => 'Nein, ich habe schon gespendet', - 'DonateEnticement' => 'Sie benutzen ZoneMinder nun schon eine Weile und es ist hoffentlich eine nützliche Applikation zur Verbesserung Ihrer Heim- oder Arbeitssicherheit. Obwohl ZoneMinder eine freie Open-Source-Software ist und bleiben wird, entstehen Kosten bei der Entwicklung und dem Support.

Falls Sie ZoneMinder für Weiterentwicklung in der Zukunft unterstützen möchten, denken Sie bitte über eine Spende für das Projekt unter der Webadresse http://www.zoneminder.com/donate.html oder über nachfolgend stehende Option nach. Spenden sind, wie der Name schon sagt, immer freiwillig. Dem Projekt helfen kleine genauso wie größere Spenden sehr weiter und ein herzlicher Dank ist jedem Spender sicher.

Vielen Dank dafür, dass sie ZoneMinder benutzen. Vergessen Sie nicht die Foren unter ZoneMinder.com, um Support zu erhalten und Ihre Erfahrung mit ZoneMinder zu verbessern!', + 'DonateEnticement' => 'Sie benutzen ZoneMinder nun schon eine Weile und es ist hoffentlich eine nützliche Applikation zur Verbesserung Ihrer Heim- oder Arbeitssicherheit. Obwohl ZoneMinder eine freie Open-Source-Software ist und bleiben wird, entstehen Kosten bei der Entwicklung und dem Support.

Falls Sie ZoneMinder für Weiterentwicklung in der Zukunft unterstützen möchten, denken Sie bitte über eine Spende für das Projekt unter der Webadresse https://zoneminder.com/donate/ oder über nachfolgend stehende Option nach. Spenden sind, wie der Name schon sagt, immer freiwillig. Dem Projekt helfen kleine genauso wie größere Spenden sehr weiter und ein herzlicher Dank ist jedem Spender sicher.

Vielen Dank dafür, dass sie ZoneMinder benutzen. Vergessen Sie nicht die Foren unter ZoneMinder.com, um Support zu erhalten und Ihre Erfahrung mit ZoneMinder zu verbessern!', 'DonateRemindDay' => 'Noch nicht, erinnere mich in einem Tag noch mal.', 'DonateRemindHour' => 'Noch nicht, erinnere mich in einer Stunde noch mal.', 'DonateRemindMonth' => 'Noch nicht, erinnere mich in einem Monat noch mal.', @@ -282,9 +299,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Noch nicht, erinnere mich in einer Woche noch mal.', 'DonateYes' => 'Ja, ich möchte jetzt spenden.', 'Download' => 'Download', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Monitornamen Duplizieren', // Added - 2009-03-31 'Duration' => 'Dauer', 'Edit' => 'Bearbeiten', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'E-Mail', 'EnableAlarms' => 'Alarme aktivieren', 'Enabled' => 'Aktiviert', @@ -301,6 +320,7 @@ $SLANG = array( 'Events' => 'Ereignisse', 'Exclude' => 'Ausschluss;', 'Execute' => 'Ausführen', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Exportieren', 'ExportDetails' => 'Exportiere Ereignis-Details', 'ExportFailed' => 'Exportieren fehlgeschlagen', @@ -330,8 +350,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Ausführen bei allen Treffern', 'FilterLog' => 'Log filtern', // Added - 2015-04-18 'FilterMessageEvents' => 'Detaillierte Nachricht zu allen Treffern', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filter-Pixel', 'FilterUnset' => 'Sie müssen eine Breite und Höhe für das Filter angeben', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Hochladen aller Treffer', 'FilterVideoEvents' => 'Video für alle Treffer erstellen', 'Filters' => 'Filter', @@ -356,6 +378,7 @@ $SLANG = array( 'Function' => 'Funktion', 'Gain' => 'Verstärkung', 'General' => 'Allgemeines', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Erzeuge Video', 'GeneratingVideo' => 'Erzeuge Video...', 'GoToZoneMinder' => 'Besuche ZoneMinder.com', @@ -376,6 +399,7 @@ $SLANG = array( 'High' => 'hohe', 'HighBW' => 'Hohe B/W', 'Home' => 'Home', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Stunde', 'Hue' => 'Farbton', 'Id' => 'ID', @@ -400,6 +424,7 @@ $SLANG = array( 'Line' => 'Zeile', // Added - 2011-06-16 'LinkedMonitors' => 'Verbundene Monitore', 'List' => 'Liste', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Last', 'Local' => 'Lokal', 'Log' => 'Log', // Added - 2011-06-16 @@ -486,6 +511,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'Die nachfolgende Liste zeigt erkannte Analog- und Netzwerkkameras, ob sie bereits genutzt werden und ob sie zur Auswahl verfügbar sind.

Wähle den gewünschten Eintrag aus der folgenden Liste.

Bitte Beachten: Nicht alle Kameras können erkannt werden. Die Auswahl einer Kamera kann bereits eingetragene Werte im aktuellen Monitor überschreiben.

', // Added - 2009-03-31 'Monitors' => 'Monitore', 'Montage' => 'Montage', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Monat', 'More' => 'Mehr', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -512,6 +538,7 @@ $SLANG = array( 'Next' => 'Nächstes', 'No' => 'Nein', 'NoDetectedCameras' => 'Keine Kameras erkannt', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Es gibt keine Aufnahmen von diesem Ereignis.', 'NoGroup' => 'Keine Gruppe', 'NoSavedFilters' => 'Keine gespeicherten Filter', @@ -530,6 +557,8 @@ $SLANG = array( 'OpGt' => 'groesser als', 'OpGtEq' => 'groesser oder gleich wie', 'OpIn' => 'in Satz', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'kleiner als', 'OpLtEq' => 'kleiner oder gleich wie', 'OpMatches' => 'zutreffend', @@ -539,6 +568,7 @@ $SLANG = array( 'Open' => 'öffnen', 'OptionHelp' => 'Hilfe', 'OptionRestartWarning' => 'Veränderungen werden erst nach einem Neustart des Programms aktiv.\nFür eine sofortige änderung starten Sie das Programm bitte neu.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Optionen', 'OrEnterNewName' => 'oder neuen Namen eingeben', 'Order' => 'Reihenfolge', @@ -576,9 +606,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'Die folgende Liste zeigt die verfügbaren Streamingprofile der ausgewählten Kamera.

Wähle den gewünschten Eintrag aus der folgenden Liste.

Bitte Beachten: Zoneminder kann keine zusätzlichen Profile konfigurieren. Die Auswahl einer Kamera kann bereits eingetragene Werte im aktuellen Monitor überschreiben.

', // Added - 2015-04-18 'Progress' => 'Fortschritt', // Added - 2015-04-18 'Protocol' => 'Protokoll', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Abspielgeschwindigkeit', 'Real' => 'Real', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Aufnahme', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Referenz-Bildblende', 'Refresh' => 'Aktualisieren', 'Remote' => 'Remote', @@ -594,6 +628,7 @@ $SLANG = array( 'ReplayAll' => 'Alle Ereignisse', 'ReplayGapless' => 'Lückenlose Ereignisse', 'ReplaySingle' => 'Einzelereignis', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Zurücksetzen', 'ResetEventCounts' => 'Lösche Ereignispunktzahl', 'Restart' => 'Neustart', @@ -612,6 +647,7 @@ $SLANG = array( 'Save' => 'Speichern', 'SaveAs' => 'Speichere als', 'SaveFilter' => 'Speichere Filter', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Skalierung', 'Score' => 'Wertung', 'Secs' => 'Sekunden', @@ -628,6 +664,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Zeige Filterfenster', 'ShowTimeline' => 'Zeige Zeitstrahl', 'SignalCheckColour' => 'Farbe des Signalchecks', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Größe', 'SkinDescription' => 'Wähle den standard Skin für diesen Computer.', // Added - 2011-01-30 'Sleep' => 'Schlaf', @@ -647,6 +684,10 @@ $SLANG = array( 'State' => 'Status', 'Stats' => 'Statistik', 'Status' => 'Status', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Stufe', 'StepBack' => 'Einen Schritt rückwärts', 'StepForward' => 'Einen Schritt vorwärts', @@ -657,6 +698,8 @@ $SLANG = array( 'Stills' => 'Standbilder', 'Stop' => 'Stop', 'Stopped' => 'Gestoppt', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Stream', 'StreamReplayBuffer' => 'Stream-Wiedergabe-Bildpuffer', 'Submit' => 'Absenden', @@ -676,9 +719,9 @@ $SLANG = array( 'TimelineTip4' => 'Verwenden Sie die Steuerelemente unten, um zu Zoomen oder navigieren Sie vorwärts und rückwärts durch die Zeit.', // Added 2013.08.15. 'Timestamp' => 'Zeitstempel', 'TimestampLabelFormat' => 'Format des Zeitstempels', + 'TimestampLabelSize' => 'Schriftgröße', 'TimestampLabelX' => 'Zeitstempel-X', 'TimestampLabelY' => 'Zeitstempel-Y', - 'TimestampLabelSize' => 'Schriftgröße', 'Today' => 'Heute', 'Tools' => 'Werkzeuge', 'Total' => 'Insgesamt', // Added - 2011-06-16 @@ -723,6 +766,7 @@ $SLANG = array( 'VideoGenParms' => 'Parameter der Videoerzeugung', 'VideoGenSucceeded' => 'Videoerzeugung erfolgreich!', 'VideoSize' => 'Videogröße', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Ansicht', 'ViewAll' => 'Alles ansehen', 'ViewEvent' => 'Zeige Ereignis', @@ -732,6 +776,7 @@ $SLANG = array( 'Watch' => 'Beobachte', 'Web' => 'Web', 'WebColour' => 'Webfarbe', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Woche', 'White' => 'Weiß', 'WhiteBalance' => 'Weiß-Abgleich', diff --git a/web/lang/default.php b/web/lang/default.php new file mode 100644 index 000000000..3f29757d7 --- /dev/null +++ b/web/lang/default.php @@ -0,0 +1,43 @@ + 'Privacy', + 'PrivacyAbout' => 'About', + 'PrivacyAboutText' => 'Since 2002, ZoneMinder has been the premier free and open-source Video Management System (VMS) solution for Linux platforms. ZoneMinder is supported by the community and is managed by those who choose to volunteer their spare time to the project. The best way to improve ZoneMinder is to get involved.', + 'PrivacyContact' => 'Contact', + 'PrivacyContactText' => 'Please contact us here for any questions regarding our privacy policy or to have your information removed.

For support, there are three primary ways to engage with the community:

Our Github forum is only for bug reporting. Please use our user forum or slack channel for all other questions or comments.

', + 'PrivacyCookies' => 'Cookies', + 'PrivacyCookiesText' => 'Whether you use a web browser or a mobile app to communicate with the ZoneMinder server, a ZMSESSID cookie is created on the client to uniquely identify a session with the ZoneMinder server. ZmCSS and zmSkin cookies are created to remember your style and skin choices.', + 'PrivacyTelemetry' => 'Telemetry', + 'PrivacyTelemetryText' => 'Because ZoneMinder is open-source, anyone can install it without registering. This makes it difficult to answer questions such as: how many systems are out there, what is the largest system out there, what kind of systems are out there, or where are these systems located? Knowing the answers to these questions, helps users who ask us these questions, and it helps us set priorities based on the majority user base.', + 'PrivacyTelemetryList' => 'The ZoneMinder Telemetry daemon collects the following data about your system:
  • A unique identifier (UUID)
  • City based location is gathered by querying ipinfo.io. City, region, country, latitude, and longitude parameters are saved. The latitude and longitude coordinates are accurate down to the city or town level only!
  • Current time
  • Total number of monitors
  • Total number of events
  • System architecture
  • Operating system kernel, distro, and distro version
  • Version of ZoneMinder
  • Total amount of memory
  • Number of cpu cores
', + 'PrivacyMonitorList' => 'The following configuration parameters from each monitor are collected:
  • Id
  • Name
  • Type
  • Function
  • Width
  • Height
  • Colours
  • MaxFPS
  • AlarmMaxFPS
', + 'PrivacyConclusionText' => 'We are NOT collecting any image specific data from your cameras. We don’t know what your cameras are watching. This data will not be sold or used for any purpose not stated herein. By clicking accept, you agree to send us this data to help make ZoneMinder a better product. By clicking decline, you can still freely use ZoneMinder and all its features.', +); + +?> diff --git a/web/lang/dk_dk.php b/web/lang/dk_dk.php index 605f9fb19..045b5345e 100644 --- a/web/lang/dk_dk.php +++ b/web/lang/dk_dk.php @@ -72,15 +72,6 @@ header( "Content-Type: text/html; charset=utf-8" ); // Simple String Replacements $SLANG = array( - 'SystemLog' => 'System Log', - 'DateTime' => 'Dato/Tid', - 'Component' => 'Komponent', - 'Pid' => 'PID', - 'Level' => 'Niveau', - 'Message' => 'Meddelelse', - 'Line' => 'Linie', - 'More' => 'Mere', - 'Clear' => 'Slet', '24BitColour' => '24 bit farve', '32BitColour' => '32 bit farve', '8BitGrey' => '8 bit gråskala', @@ -89,6 +80,7 @@ $SLANG = array( 'AddNewControl' => 'Tilføj Ny Kontrol', 'AddNewMonitor' => 'Tilføj Ny Monitor', 'AddNewServer' => 'Tilføj Ny Server', + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Tilføj Ny Bruger', 'AddNewZone' => 'Tilføj Ny Zone', 'Alarm' => 'Alarm', @@ -98,8 +90,8 @@ $SLANG = array( 'AlarmLimits' => 'Alarm Grænser', 'AlarmMaximumFPS' => 'Alarm Maksimum FPS', 'AlarmPx' => 'Alarm Px', - 'AlarmRefImageBlendPct' => 'Alarm Reference Billede Blandings %', 'AlarmRGBUnset' => 'Du skal vælge en alarm RGB farve', + 'AlarmRefImageBlendPct' => 'Alarm Reference Billede Blandings %', 'Alert' => 'Advarsel', 'All' => 'Alle', 'AnalysisFPS' => 'Analyse FPS', @@ -107,37 +99,45 @@ $SLANG = array( 'Apply' => 'Udfør', 'ApplyingStateChange' => 'Udfører tilstandsændring', 'ArchArchived' => 'Kun arkiverede', + 'ArchUnarchived' => 'Kun ikke-arkiverede', 'Archive' => 'Arkivér', 'Archived' => 'Arkiverede', - 'ArchUnarchived' => 'Kun ikke-arkiverede', 'Area' => 'Område', 'AreaUnits' => 'Område (px/%)', 'AttrAlarmFrames' => 'Alarm Rammer', 'AttrArchiveStatus' => 'Arkiverings Status', 'AttrAvgScore' => 'Middel Score', 'AttrCause' => 'Årsag', - 'AttrDate' => 'Dato', - 'AttrDateTime' => 'Dato/Tid', 'AttrDiskBlocks' => 'Disk Blokke', 'AttrDiskPercent' => 'Disk Procent', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Varighed', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Rammer', 'AttrId' => 'Id', 'AttrMaxScore' => 'Max. Score', 'AttrMonitorId' => 'Monitor Id', 'AttrMonitorName' => 'Monitor Navn', - 'AttrServer' => 'Server', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Navn', 'AttrNotes' => 'Noter', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'System Belastning', - 'AttrTime' => 'Tid', 'AttrTotalScore' => 'Total Score', - 'AttrWeekday' => 'Ugedag', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Auto Stop Timeout', 'Available' => 'Tilgængelig', 'AvgBrScore' => 'Middel
Score', - 'Available' => 'Tilgængelig', 'Background' => 'Baggrund', 'BackgroundFilter' => 'Kør filteret i baggrunden', 'BadAlarmFrameCount' => 'Antal alarm rammer skal være et positivt heltal', @@ -145,20 +145,20 @@ $SLANG = array( 'BadAnalysisFPS' => 'Analyse FPS skal være et positivt heltal eller flydende tal', 'BadAnalysisUpdateDelay'=> 'Analyse opdaterings forsinkelse skal være et heltal på 0 eller mere', 'BadChannel' => 'Kanal skal sættes til et heltal på 0 eller mere', + 'BadColours' => 'Målfarven skal sættes til en gyldig værdi', 'BadDevice' => 'Enhed skal sættes til en gyldig værdi', - 'BadFormat' => 'Format skal sættes til en gyldig værdi', 'BadFPSReportInterval' => 'Antal FPS report interval buffere skal være et heltal på 0 eller mere', + 'BadFormat' => 'Format skal sættes til en gyldig værdi', 'BadFrameSkip' => 'Antal Frame skip skal være et heltal på 0 eller mere', - 'BadMotionFrameSkip' => 'Antal Motion Frame skip skal være et heltal på 0 eller mere', 'BadHeight' => 'Højde skal sættes til en gyldig værdi', 'BadHost' => 'Host skal vare en gyldig IP adresse eller hostname, inkludér ikke http://', 'BadImageBufferCount' => 'Billed buffer størrelse skal være et heltal på 10 eller mere', 'BadLabelX' => 'Mærkat X co-ordinaten skal sættes til et heltal på 0 eller mere', 'BadLabelY' => 'Mærkat Y co-ordinaten skal sættes til et heltal på 0 eller mere', 'BadMaxFPS' => 'Maximum FPS skal være et positivt heltal eller flydende tal', + 'BadMotionFrameSkip' => 'Antal Motion Frame skip skal være et heltal på 0 eller mere', 'BadNameChars' => 'Navne kan kun indeholde alfanumeriske tegn samt mellemrum, bindestreg og understregning', 'BadPalette' => 'Palette skal sættes til en gyldig værdi', - 'BadColours' => 'Målfarven skal sættes til en gyldig værdi', 'BadPath' => 'Sti skal sættes til en gyldig værdi', 'BadPort' => 'Port skal sættes til et gyldigt nummer', 'BadPostEventCount' => 'Antal rammer efter hændelsen skal være et heltal på 0 eller mere', @@ -166,62 +166,66 @@ $SLANG = array( 'BadRefBlendPerc' => 'Reference blandings procentdelen skal være et positivt heltal', 'BadSectionLength' => 'Sektionslængden skal være et heltal på 30 eller mere', 'BadSignalCheckColour' => 'Signal check farve skal være en gyldig RGB farve streng', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer' => 'Videostrøm genspilsbufferen skal sættes til et heltal på 0 eller mere', 'BadWarmupCount' => 'Opvarmnings rammer skal være et heltal på 0 eller mere', 'BadWebColour' => 'Web farve skal være en gyldigt web farve streng', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Bredde skal sættes til en gyldig værdi', 'Bandwidth' => 'Båndbredde', 'BandwidthHead' => 'Båndbredde', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing 'BlobPx' => 'Blob Px', - 'Blobs' => 'Blobs', 'BlobSizes' => 'Blob Størrelser', + 'Blobs' => 'Blobs', 'Brightness' => 'Lysstyrke', 'Buffer' => 'Buffer', 'Buffers' => 'Buffere', + 'CSSDescription' => 'SKift standard css for denne computer', 'CanAutoFocus' => 'Can Auto Focus', 'CanAutoGain' => 'Can Auto Gain', 'CanAutoIris' => 'Can Auto Iris', 'CanAutoWhite' => 'Can Auto White Bal.', 'CanAutoZoom' => 'Can Auto Zoom', - 'Cancel' => 'Fortryd', - 'CancelForcedAlarm' => 'Fortryd Tvungen Alarm', - 'CanFocusAbs' => 'Can Focus Absolute', 'CanFocus' => 'Can Focus', + 'CanFocusAbs' => 'Can Focus Absolute', 'CanFocusCon' => 'Can Focus Continuous', 'CanFocusRel' => 'Can Focus Relative', - 'CanGainAbs' => 'Can Gain Absolute', 'CanGain' => 'Can Gain ', + 'CanGainAbs' => 'Can Gain Absolute', 'CanGainCon' => 'Can Gain Continuous', 'CanGainRel' => 'Can Gain Relative', - 'CanIrisAbs' => 'Can Iris Absolute', 'CanIris' => 'Can Iris', + 'CanIrisAbs' => 'Can Iris Absolute', 'CanIrisCon' => 'Can Iris Continuous', 'CanIrisRel' => 'Can Iris Relative', - 'CanMoveAbs' => 'Can Move Absolute', 'CanMove' => 'Can Move', + 'CanMoveAbs' => 'Can Move Absolute', 'CanMoveCon' => 'Can Move Continuous', 'CanMoveDiag' => 'Can Move Diagonally', 'CanMoveMap' => 'Can Move Mapped', 'CanMoveRel' => 'Can Move Relative', 'CanPan' => 'Can Pan' , 'CanReset' => 'Can Reset', + 'CanReboot' => 'Can Reboot', 'CanSetPresets' => 'Can Set Presets', 'CanSleep' => 'Can Sleep', 'CanTilt' => 'Can Tilt', 'CanWake' => 'Can Wake', + 'CanWhite' => 'Can White Balance', 'CanWhiteAbs' => 'Can White Bal. Absolute', 'CanWhiteBal' => 'Can White Bal.', - 'CanWhite' => 'Can White Balance', 'CanWhiteCon' => 'Can White Bal. Continuous', 'CanWhiteRel' => 'Can White Bal. Relative', - 'CanZoomAbs' => 'Can Zoom Absolute', 'CanZoom' => 'Can Zoom', + 'CanZoomAbs' => 'Can Zoom Absolute', 'CanZoomCon' => 'Can Zoom Continuous', 'CanZoomRel' => 'Can Zoom Relative', + 'Cancel' => 'Fortryd', + 'CancelForcedAlarm' => 'Fortryd Tvungen Alarm', 'CaptureHeight' => 'Capture Højde', 'CaptureMethod' => 'Capture Metode', - 'CaptureResolution' => 'Capture Opløsning', 'CapturePalette' => 'Capture Palette', + 'CaptureResolution' => 'Capture Opløsning', 'CaptureWidth' => 'Capture Bredde', 'Cause' => 'Årsag', 'CheckMethod' => 'Alarm Check Metode', @@ -230,10 +234,13 @@ $SLANG = array( 'ChooseLogFormat' => 'Vælg et lognings format', 'ChooseLogSelection' => 'Vælg et lognings udvælgelse', 'ChoosePreset' => 'Vælg Forudindstilling', + 'Clear' => 'Slet', 'CloneMonitor' => 'Klon Monitor', 'Close' => 'Luk', 'Colour' => 'Farve', 'Command' => 'Kommando', + 'Component' => 'Komponent', + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Konfigurer', 'ConfiguredFor' => 'Konfigureret for', 'ConfirmDeleteEvents' => 'Er du sikker på, at du vil slette de markerede hændelser?', @@ -244,62 +251,64 @@ $SLANG = array( 'ContactAdmin' => 'Venligst kontakt din administrator for detaljer.', 'Continue' => 'Fortsæt', 'Contrast' => 'Kontrast', + 'Control' => 'Control', 'ControlAddress' => 'Control Address', 'ControlCap' => 'Control Capability', 'ControlCaps' => 'Control Capabilities', - 'Control' => 'Control', 'ControlDevice' => 'Control Device', - 'Controllable' => 'Controllable', 'ControlType' => 'Control Type', + 'Controllable' => 'Controllable', 'Current' => 'Nuværende', 'Cycle' => 'Cyklisk', 'CycleWatch' => 'Cyklisk Overvågning', + 'DateTime' => 'Dato/Tid', 'Day' => 'Dag', 'Debug' => 'Fejlfind', 'DefaultRate' => 'Standard Rate', 'DefaultScale' => 'Standard Skalering', 'DefaultView' => 'Standard Visning', 'Deinterlacing' => 'Deinterlacing', - 'RTSPDescribe' => 'Brug RTSP Response Media URL', 'Delay' => 'Forsilkelse', + 'Delete' => 'Slet', 'DeleteAndNext' => 'Slet & Næste', 'DeleteAndPrev' => 'Slet & Forrige', - 'Delete' => 'Slet', 'DeleteSavedFilter' => 'Slet gemt filter', 'Description' => 'Beskrivelse', 'DetectedCameras' => 'Fundne Kameraer', 'DetectedProfiles' => 'Fundne Profiler', + 'Device' => 'Enheds', 'DeviceChannel' => 'Enheds Kanal', 'DeviceFormat' => 'Enheds Format', 'DeviceNumber' => 'Enheds Number', 'DevicePath' => 'Sti Til Enhed', - 'Device' => 'Enheds', 'Devices' => 'Enheder', 'Dimensions' => 'Dimensioner', 'DisableAlarms' => 'Deaktiver Alarmer', 'Disk' => 'Disk', 'Display' => 'Display', 'Displaying' => 'Displaying', - 'DonateAlready' => 'Nej, jeg har allerede doneret', - 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', + 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'Venligst Donér', + 'DonateAlready' => 'Nej, jeg har allerede doneret', + 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', 'DonateRemindDay' => 'Ikke endnu, påmind igen on 1 dag', 'DonateRemindHour' => 'Ikke endnu, påmind igen on 1 time', 'DonateRemindMonth' => 'Ikke endnu, påmind igen on 1 måned', 'DonateRemindNever' => 'Nej, jeg ønsker ikke at donere, påmind ikke igen', 'DonateRemindWeek' => 'Ikke endnu, påmind igen on 1 uge', 'DonateYes' => 'Ja, jeg vil gerne donere nu', - 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Download' => 'Download', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Dupliket Monitor Navn', 'Duration' => 'Varighed', 'Edit' => 'Ret', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Aktivér Alarmer', 'Enabled' => 'Virksom', 'EnterNewFilterName' => 'Indtast nyt filternavn', - 'ErrorBrackets' => 'Fejl, venligst check, at du har samme antal open og lukke klammer', 'Error' => 'Fejl', + 'ErrorBrackets' => 'Fejl, venligst check, at du har samme antal open og lukke klammer', 'ErrorValidValue' => 'Fejl, venligst check at alle parametre har en gyldig værdi', 'Etc' => 'etc', 'Event' => 'Hændelse', @@ -310,9 +319,9 @@ $SLANG = array( 'Events' => 'Hændelser', 'Exclude' => 'Ekskluder', 'Execute' => 'Udfør', - 'ExportDetails' => 'Exporter Hændelses Detaljer', 'Exif' => 'Indlejre EXIF data i billede', 'Export' => 'Exporter', + 'ExportDetails' => 'Exporter Hændelses Detaljer', 'ExportFailed' => 'Export Mislykkedes', 'ExportFormat' => 'Export Fil Format', 'ExportFormatTar' => 'Tar', @@ -320,53 +329,55 @@ $SLANG = array( 'ExportFrames' => 'Exporter Ramme Detaljer', 'ExportImageFiles' => 'Exporter billed filer', 'ExportLog' => 'Export Log', - 'Exporting' => 'Exporterer', 'ExportMiscFiles' => 'Exporter Andre Filer (hvis tilstede)', 'ExportOptions' => 'Export Indstillinger', 'ExportSucceeded' => 'Export Lykkedes', 'ExportVideoFiles' => 'Exporter Video Filer (hvis tilstede)', + 'Exporting' => 'Exporterer', + 'FPS' => 'fps', + 'FPSReportInterval' => 'FPS Rapport Interval', + 'FTP' => 'FTP', 'Far' => 'Fjern', 'FastForward' => 'Hurtigt Frem', 'Feed' => 'Feed', 'Ffmpeg' => 'Ffmpeg', 'File' => 'Fil', + 'Filter' => 'Filter', 'FilterArchiveEvents' => 'Arkiver alle matchende', 'FilterDeleteEvents' => 'Slet alle matchende', 'FilterEmailEvents' => 'Email detaljer for alle matchende', 'FilterExecuteEvents' => 'Udfør kommando for alle matchende', 'FilterLog' => 'Filter log', 'FilterMessageEvents' => 'Meddel detaljer for alle matchende', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filter Px', - 'Filter' => 'Filter', - 'Filters' => 'Filtre', 'FilterUnset' => 'Du skal angive filter bredde og højde', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Upload alle match', 'FilterVideoEvents' => 'Opret video for alle match', + 'Filters' => 'Filtre', 'First' => 'Første', 'FlippedHori' => 'Spejlet Horizontalt', 'FlippedVert' => 'Spejlet Vertikalt', - 'FnNone' => 'None', // Added 2013.08.16. - 'FnMonitor' => 'Monitor', // Added 2013.08.16. - 'FnModect' => 'Modect', // Added 2013.08.16. - 'FnRecord' => 'Record', // Added 2013.08.16. 'FnMocord' => 'Mocord', // Added 2013.08.16. + 'FnModect' => 'Modect', // Added 2013.08.16. + 'FnMonitor' => 'Monitor', // Added 2013.08.16. 'FnNodect' => 'Nodect', // Added 2013.08.16. + 'FnNone' => 'None', // Added 2013.08.16. + 'FnRecord' => 'Record', // Added 2013.08.16. 'Focus' => 'Focus', 'ForceAlarm' => 'Force Alarm', 'Format' => 'Format', - 'FPS' => 'fps', - 'FPSReportInterval' => 'FPS Rapport Interval', 'Frame' => 'Ramme', 'FrameId' => 'Ramme Id', 'FrameRate' => 'Billedhastighed', - 'Frames' => 'Rammer', 'FrameSkip' => 'Spring over antal rammer', - 'MotionFrameSkip' => 'Spring over antal bevægelsesrammer', - 'FTP' => 'FTP', + 'Frames' => 'Rammer', 'Func' => 'Funk', 'Function' => 'Funktion', 'Gain' => 'Gain', 'General' => 'Generelt', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Generer Video', 'GeneratingVideo' => 'Genererer Video', 'GoToZoneMinder' => 'Gå til ZoneMinder.com', @@ -384,8 +395,8 @@ $SLANG = array( 'HasTurboTilt' => 'Has Turbo Tilt', 'HasWhiteSpeed' => 'Has White Bal. Speed', 'HasZoomSpeed' => 'Has Zoom Speed', - 'HighBW' => 'High B/W', 'High' => 'Høj', + 'HighBW' => 'High B/W', 'Home' => 'Hjemme', 'Hostname' => 'Hostname', 'Hour' => 'Time', @@ -393,11 +404,11 @@ $SLANG = array( 'Id' => 'Id', 'Idle' => 'Afventende', 'Ignore' => 'Ignorer', - 'ImageBufferSize' => 'Billed Buffer Størrelse (rammer)', 'Image' => 'Billede', + 'ImageBufferSize' => 'Billed Buffer Størrelse (rammer)', 'Images' => 'Billeder', - 'Include' => 'Inkluder', 'In' => 'I', + 'Include' => 'Inkluder', 'Inverted' => 'Inverteret', 'Iris' => 'Blænde', 'KeyString' => 'Nøgle Streng', @@ -405,26 +416,30 @@ $SLANG = array( 'Language' => 'Sprog', 'Last' => 'Sidste', 'Layout' => 'Layout', + 'Level' => 'Niveau', 'Libvlc' => 'Libvlc', 'LimitResultsPost' => 'resultater', // This is used at the end of the phrase 'Limit to first N results only' 'LimitResultsPre' => 'Begræns til kun de første', // This is used at the beginning of the phrase 'Limit to first N results only' + 'Line' => 'Linie', 'LinkedMonitors' => 'Sammenkædede Monitorer', 'List' => 'Liste', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Belastning', 'Local' => 'Lokal', 'Log' => 'Log', - 'Logs' => 'Logs', - 'Logging' => 'Logning', 'LoggedInAs' => 'Logget ind som', + 'Logging' => 'Logning', 'LoggingIn' => 'Logger ind', 'Login' => 'Logind', 'Logout' => 'Logud', - 'LowBW' => 'Lav B/W', + 'Logs' => 'Logs', 'Low' => 'Lav', + 'LowBW' => 'Lav B/W', 'Main' => 'Hoved', 'Man' => 'Man', 'Manual' => 'Manuel', 'Mark' => 'Markér', + 'Max' => 'Max', 'MaxBandwidth' => 'Max Båndbredde', 'MaxBrScore' => 'Max.
Score', 'MaxFocusRange' => 'Max Focus Range', @@ -433,11 +448,9 @@ $SLANG = array( 'MaxGainRange' => 'Max Gain Range', 'MaxGainSpeed' => 'Max Gain Speed', 'MaxGainStep' => 'Max Gain Step', - 'MaximumFPS' => 'Maximum FPS', 'MaxIrisRange' => 'Max Iris Range', 'MaxIrisSpeed' => 'Max Iris Speed', 'MaxIrisStep' => 'Max Iris Step', - 'Max' => 'Max', 'MaxPanRange' => 'Max Pan Range', 'MaxPanSpeed' => 'Max Pan Speed', 'MaxPanStep' => 'Max Pan Step', @@ -450,8 +463,10 @@ $SLANG = array( 'MaxZoomRange' => 'Max Zoom Range', 'MaxZoomSpeed' => 'Max Zoom Speed', 'MaxZoomStep' => 'Max Zoom Step', - 'MediumBW' => 'Medium B/W', + 'MaximumFPS' => 'Maximum FPS', 'Medium' => 'Medium', + 'MediumBW' => 'Medium B/W', + 'Message' => 'Meddelelse', 'MinAlarmAreaLtMax' => 'Minimum alarm område skal være mindre end maksimum', 'MinAlarmAreaUnset' => 'Du skal angive det minimale antal alarm pixels', 'MinBlobAreaLtMax' => 'Minimum blob område skal være mindre end maksimum', @@ -487,22 +502,24 @@ $SLANG = array( 'MinZoomStep' => 'Min Zoom Step', 'Misc' => 'Diverse', 'Mode' => 'Mode', - 'MonitorIds' => 'Monitor Ids', 'Monitor' => 'Monitor', - 'MonitorPresetIntro' => 'Vælg en passende forudindstilling fra listen herunder.

Vær opmærksom på, at dette kan overskrive værdier, du allerede har angivet for den aktuelle monitor.

', + 'MonitorIds' => 'Monitor Ids', 'MonitorPreset' => 'Monitor Forudindstillinger', - 'MonitorProbeIntro' => 'Listen herunder viser fundne analoge og nætværks kameraer samt hvorvidt de allerede er i brug eller tilgængelige for udvælgelse.

Vælg det ønskede fra listen herunder.

Vær opmærksom på, at muligvis ikke alle kameraer er fundet og at valg af et kamera kan overskrive værdier, du allerede har angivet for den aktuelle monitor.

', + 'MonitorPresetIntro' => 'Vælg en passende forudindstilling fra listen herunder.

Vær opmærksom på, at dette kan overskrive værdier, du allerede har angivet for den aktuelle monitor.

', 'MonitorProbe' => 'Monitor Probe', + 'MonitorProbeIntro' => 'Listen herunder viser fundne analoge og nætværks kameraer samt hvorvidt de allerede er i brug eller tilgængelige for udvælgelse.

Vælg det ønskede fra listen herunder.

Vær opmærksom på, at muligvis ikke alle kameraer er fundet og at valg af et kamera kan overskrive værdier, du allerede har angivet for den aktuelle monitor.

', 'Monitors' => 'Monitorer', 'Montage' => 'Montage', 'MontageReview' => 'Montage Review', 'Month' => 'Måned', + 'More' => 'Mere', + 'MotionFrameSkip' => 'Spring over antal bevægelsesrammer', 'Move' => 'Bevæg', - 'MtgDefault' => 'Standard', // Added 2013.08.15. 'Mtg2widgrd' => '2-bred gitter', // Added 2013.08.15. 'Mtg3widgrd' => '3-bred gitter', // Added 2013.08.15. - 'Mtg4widgrd' => '4-bred gitter', // Added 2013.08.15. 'Mtg3widgrx' => '3-bred gitter, skaleret, forstørret ved alarm', // Added 2013.08.15. + 'Mtg4widgrd' => '4-bred gitter', // Added 2013.08.15. + 'MtgDefault' => 'Standard', // Added 2013.08.15. 'MustBeGe' => 'Skal være større end eller lig med', 'MustBeLe' => 'Skal være mindre end eller lig med', 'MustConfirmPassword' => 'Du skal bekræfte adgangskoden', @@ -511,53 +528,55 @@ $SLANG = array( 'Name' => 'Navn', 'Near' => 'Nær', 'Network' => 'Netværk', + 'New' => 'Ny', 'NewGroup' => 'Ny Gruppe', 'NewLabel' => 'Ny Mærkat', - 'New' => 'Ny', 'NewPassword' => 'Ny Adgangskode', 'NewState' => 'Ny Tilstand', 'NewUser' => 'Ny bruger', 'Next' => 'Næste', + 'No' => 'Nej', 'NoDetectedCameras' => 'Ingen Detected Cameras', 'NoDetectedProfiles' => 'Ingen Fundne Profiler', 'NoFramesRecorded' => 'Der er ingen billeder optaget for denne hændelse', 'NoGroup' => 'Ingen gruppe', - 'NoneAvailable' => 'Ingen tilgængelig', - 'None' => 'Ingen', - 'No' => 'Nej', - 'Normal' => 'Normalt', 'NoSavedFilters' => 'IngenGemteFiltre', 'NoStatisticsRecorded' => 'Der er ingen statistik noteret for denne hændelse/ramme', + 'None' => 'Ingen', + 'NoneAvailable' => 'Ingen tilgængelig', + 'Normal' => 'Normalt', 'Notes' => 'Noter', 'NumPresets' => 'Num Forudinst.', 'Off' => 'Fra', 'On' => 'Til', + 'OnvifCredentialsIntro' => 'Venligst lever brugernavn og adgangskodefor de valgte kamera.
Hvis der ikke er oprettet nogen bruger for kameraet, så vil brugeren givet her blive oprettet med den angivne adgangskode.

', 'OnvifProbe' => 'ONVIF', 'OnvifProbeIntro' => 'Listen nedenfor viser fundne ONVIF kameraer samt hvorvidt de allerede er i brug eller tilgængelige for udvælgelse.

Vælg det ønskede fra listen herunder.

Vær opmærksom på, at muligvis ikke alle kameraer er fundet og at valg af et kamera kan overskrive værdier, du allerede har angivet for den aktuelle monitor.

', - 'OnvifCredentialsIntro' => 'Venligst lever brugernavn og adgangskodefor de valgte kamera.
Hvis der ikke er oprettet nogen bruger for kameraet, så vil brugeren givet her blive oprettet med den angivne adgangskode.

', - 'Open' => 'Åben', 'OpEq' => 'lig med', - 'OpGtEq' => 'større end eller lig med', 'OpGt' => 'større end', + 'OpGtEq' => 'større end eller lig med', 'OpIn' => 'indeholdt i', - 'OpLtEq' => 'mindre end eller lig med', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'mindre end', + 'OpLtEq' => 'mindre end eller lig med', 'OpMatches' => 'matcher', 'OpNe' => 'ikke lig med', 'OpNotIn' => 'ikke indeholdt i', 'OpNotMatches' => 'matcher ikke', - 'OptionalEncoderParam' => 'Optionelle Encoder Parametre', + 'Open' => 'Åben', 'OptionHelp' => 'Indstillinger hjælp', 'OptionRestartWarning' => 'Disse ændringer har muligvis ikke fuld effekt\nmens systemet er kørende. Når du har\nafsluttet dine ændringer, skal du huske at\ngenstarte ZoneMinder.', + 'OptionalEncoderParam' => 'Optionelle Encoder Parametre', 'Options' => 'Indstillinger', - 'Order' => 'Rækkefølge', 'OrEnterNewName' => 'eller indtast nyt navn', + 'Order' => 'Rækkefølge', 'Orientation' => 'Orientering', 'Out' => 'Ud', 'OverwriteExisting' => 'Overskriv Eksisterende', 'Paged' => 'Sidevis', - 'PanLeft' => 'Pan Left', 'Pan' => 'Pan', + 'PanLeft' => 'Pan Left', 'PanRight' => 'Pan Right', 'PanTilt' => 'Pan/Tilt', 'Parameter' => 'Parameter', @@ -565,14 +584,15 @@ $SLANG = array( 'PasswordsDifferent' => 'Den nye og den bekræftende adgangskode er forskellige', 'Paths' => 'Stier', 'Pause' => 'Pause', - 'PhoneBW' => 'Telefon B/W', 'Phone' => 'Telefon', + 'PhoneBW' => 'Telefon B/W', + 'Pid' => 'PID', 'PixelDiff' => 'Pixel Forskel', 'Pixels' => 'pixels', - 'PlayAll' => 'Afspil Alle', 'Play' => 'Afspil', - 'Plugins' => 'Plugins', + 'PlayAll' => 'Afspil Alle', 'PleaseWait' => 'Vent venligst', + 'Plugins' => 'Plugins', 'Point' => 'Point', 'PostEventImageBuffer' => 'Antal Billeder Efter Hændelse', 'PreEventImageBuffer' => 'Antal Billeder Før Hændelse', @@ -585,30 +605,33 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', 'Progress' => 'Position', 'Protocol' => 'Protokol', + 'RTSPDescribe' => 'Brug RTSP Response Media URL', + 'RTSPTransport' => 'RTSP Transport Protocol', 'Rate' => 'Rate', - 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // added Sep 24 2015 - PP - 'RecordAudio' => 'Skal lydsporet gemmes sammen med en hændelse.', 'Real' => 'Naturtro', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // added Sep 24 2015 - PP 'Record' => 'Optag', + 'RecordAudio' => 'Skal lydsporet gemmes sammen med en hændelse.', 'RefImageBlendPct' => 'Reference Billede Blandings %', 'Refresh' => 'Genindlæs', + 'Remote' => 'Remote', 'RemoteHostName' => 'Remote Host Name', 'RemoteHostPath' => 'Remote Host Path', - 'RemoteHostSubPath' => 'Remote Host SubPath', 'RemoteHostPort' => 'Remote Host Port', + 'RemoteHostSubPath' => 'Remote Host SubPath', 'RemoteImageColours' => 'Remote Image Colours', 'RemoteMethod' => 'Remote Method', 'RemoteProtocol' => 'Remote Protocol', - 'Remote' => 'Remote', 'Rename' => 'Omdøb', + 'Replay' => 'Genafspil', 'ReplayAll' => 'Alle Hændelser', 'ReplayGapless' => 'Hændelser uafbrudt', - 'Replay' => 'Genafspil', 'ReplaySingle' => 'Enkelt Hændelse', - 'ResetEventCounts' => 'Nulstil Hændelses Tæller', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Nulstil', - 'Restarting' => 'Genstarter', + 'ResetEventCounts' => 'Nulstil Hændelses Tæller', 'Restart' => 'Genstart', + 'Restarting' => 'Genstarter', 'RestrictedCameraIds' => 'Restricted Camera Ids', 'RestrictedMonitors' => 'Restricted Monitors', 'ReturnDelay' => 'Return Delay', @@ -616,34 +639,33 @@ $SLANG = array( 'Rewind' => 'Hurtigt Tilbage', 'RotateLeft' => 'Roter til venstrte', 'RotateRight' => 'Roter til højre', - 'RTSPTransport' => 'RTSP Transport Protocol', 'RunLocalUpdate' => 'Kør venligst zmupdate.pl for at opdatere', 'RunMode' => 'Driftsmåde', - 'Running' => 'Kørende', 'RunState' => 'Drift Tilstand', + 'Running' => 'Kørende', + 'Save' => 'Gem', 'SaveAs' => 'Gem som', 'SaveFilter' => 'Gem Filter', - 'SaveJPEGS' => 'Gem JPEGs', - 'Save' => 'Gem', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Skaler', 'Score' => 'Score', 'Secs' => 'Sek.', 'Sectionlength' => 'Sektions længde', - 'SelectMonitors' => 'Vælg Monitorer', 'Select' => 'Vælg', 'SelectFormat' => 'Vælg Format', 'SelectLog' => 'Vælg Log', + 'SelectMonitors' => 'Vælg Monitorer', 'SelfIntersecting' => 'Polygonens kanter må ikke krydses', + 'Set' => 'Sæt', 'SetNewBandwidth' => 'Vælg ny båndbredde', 'SetPreset' => 'Set Preset', - 'Set' => 'Sæt', 'Settings' => 'Indstillinger', 'ShowFilterWindow' => 'Vis Filter Vindue', 'ShowTimeline' => 'Vis Tidslinie', 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Størrelse', 'SkinDescription' => 'SKift standard skin for denne computer', - 'CSSDescription' => 'SKift standard css for denne computer', 'Sleep' => 'Sover', 'SortAsc' => 'Voksende', 'SortBy' => 'Sortér efter', @@ -652,46 +674,53 @@ $SLANG = array( 'SourceColours' => 'Kilde Farver', 'SourcePath' => 'Kilde Sti', 'SourceType' => 'Kilde Type', + 'Speed' => 'Speed', 'SpeedHigh' => 'High Speed', 'SpeedLow' => 'Low Speed', 'SpeedMedium' => 'Medium Speed', - 'Speed' => 'Speed', 'SpeedTurbo' => 'Turbo Speed', 'Start' => 'Start', 'State' => 'Tilstand', 'Stats' => 'Stats', 'Status' => 'Status', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 + 'Step' => 'Skridt', 'StepBack' => 'Skridt Tilbage', 'StepForward' => 'Skridt Frem', 'StepLarge' => 'Langt Skridt', 'StepMedium' => 'Medium Skridt', 'StepNone' => 'Ingen Skridt', 'StepSmall' => 'Lille Skridt', - 'Step' => 'Skridt', 'Stills' => 'Stilbilleder', - 'Stopped' => 'Stoppet', 'Stop' => 'Stop', - 'StreamReplayBuffer' => 'Stream Replay Image Buffer', + 'Stopped' => 'Stoppet', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Stream', + 'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Submit' => 'Påtryk', 'System' => 'System', + 'SystemLog' => 'System Log', 'TargetColorspace' => 'Target colorspace', 'Tele' => 'Tele', 'Thumbnail' => 'Thumbnail', 'Tilt' => 'Tilt', + 'Time' => 'Tidspunkt', 'TimeDelta' => 'Tidsforskel', + 'TimeStamp' => 'Tids stempel', 'Timeline' => 'Tidslinie', 'TimelineTip1' => 'Før musen over grafen for at vise snapshot billede og detaljer om hændelsen.', // Added 2013.08.15. 'TimelineTip2' => 'Klip på de farvede områder af grafen, eller billedet, for at se hændelsen.', // Added 2013.08.15. 'TimelineTip3' => 'Klik på baggrunden for at zoome ind på en mindre tidsperiode omkring dit klik.', // Added 2013.08.15. 'TimelineTip4' => 'Brug kontrollerne nedenfor for at zoome ud eller navigere frem eller tilbage i tiden.', // Added 2013.08.15. + 'Timestamp' => 'Tidsstempel', 'TimestampLabelFormat' => 'Tidsstempel Mærkat Format', + 'TimestampLabelSize' => 'Font Størrelse', 'TimestampLabelX' => 'Tidsstempel Mærkat X', 'TimestampLabelY' => 'Tidsstempel Mærkat Y', - 'TimestampLabelSize' => 'Font Størrelse', - 'Timestamp' => 'Tidsstempel', - 'TimeStamp' => 'Tids stempel', - 'Time' => 'Tidspunkt', 'Today' => 'Idag', 'Tools' => 'Værktøjer', 'Total' => 'Total', @@ -706,25 +735,29 @@ $SLANG = array( 'Undefined' => 'Udefineret', 'Units' => 'Enheder', 'Unknown' => 'Ukendt', + 'Update' => 'Opdater', 'UpdateAvailable' => 'En opdatering til ZoneMinder er tilgængelig.', 'UpdateNotNecessary' => 'Ingen opdatering er nødvendig.', - 'Update' => 'Opdater', - 'Upload' => 'Upload', 'Updated' => 'Opdateret', - 'UsedPlugins' => 'Anvendte Plugins', + 'Upload' => 'Upload', + 'UseFilter' => 'Anvend Filter', 'UseFilterExprsPost' => ' filter udtryk', // This is used at the end of the phrase 'use N filter expressions' 'UseFilterExprsPre' => 'Anvend ', // This is used at the beginning of the phrase 'use N filter expressions' - 'UseFilter' => 'Anvend Filter', + 'UsedPlugins' => 'Anvendte Plugins', + 'User' => 'Bruger', 'Username' => 'Brugernavn', 'Users' => 'Brugere', - 'User' => 'Bruger', + 'V4L' => 'V4L', + 'V4LCapturesPerFrame' => 'Captures Per Frame', + 'V4LMultiBuffer' => 'Multi Buffering', 'Value' => 'Værdi', + 'Version' => 'Version', 'VersionIgnore' => 'Ignorer denne værdi', 'VersionRemindDay' => 'Påmind igen om 1 dag', 'VersionRemindHour' => 'Påmind igen om 1 time', 'VersionRemindNever' => 'Påmind ikke om nye versioner', 'VersionRemindWeek' => 'Påmind igen om 1 uge', - 'Version' => 'Version', + 'Video' => 'Video', 'VideoFormat' => 'Video Format', 'VideoGenFailed' => 'Video Generering Fejlede!', 'VideoGenFiles' => 'Existerende Video Filer', @@ -733,47 +766,44 @@ $SLANG = array( 'VideoGenSucceeded' => 'Video Generering Succeeded!', 'VideoSize' => 'Video Størrelse', 'VideoWriter' => 'Video Skriver', - 'Video' => 'Video', + 'View' => 'Vis', 'ViewAll' => 'Vis Alle', 'ViewEvent' => 'Vis Hændelse', 'ViewPaged' => 'Vis Sidevis', - 'View' => 'Vis', - 'V4L' => 'V4L', - 'V4LCapturesPerFrame' => 'Captures Per Frame', - 'V4LMultiBuffer' => 'Multi Buffering', 'Wake' => 'Vågen', 'WarmupFrames' => 'Opvarmningsbilleder', 'Watch' => 'Ur', - 'WebColour' => 'Web Farve', 'Web' => 'Web', + 'WebColour' => 'Web Farve', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Uge', - 'WhiteBalance' => 'Hvidbalance', 'White' => 'Hvid', + 'WhiteBalance' => 'Hvidbalance', 'Wide' => 'Bred', + 'X' => 'X', + 'X10' => 'X10', 'X10ActivationString' => 'X10 Activerings Streng', 'X10InputAlarmString' => 'X10 Input Alarm Streng', 'X10OutputAlarmString' => 'X10 Output Alarm Streng', - 'X10' => 'X10', - 'X' => 'X', + 'Y' => 'Y', 'Yes' => 'Ja', 'YouNoPerms' => 'Du har ikke tilladelse til at tilgå denne ressurse.', - 'Y' => 'Y', + 'Zone' => 'Zone', 'ZoneAlarmColour' => 'Alarm Farve (Rød/Grøn/Blå)', 'ZoneArea' => 'Zone Område', + 'ZoneExtendAlarmFrames' => 'Udvid Antal Alarm Rammer', 'ZoneFilterSize' => 'Filter Bredde/Højde (pixels)', - 'ZoneMinderLog' => 'ZoneMinder Log', 'ZoneMinMaxAlarmArea' => 'Min/Max Alarmeret Område', 'ZoneMinMaxBlobArea' => 'Min/Max Blob Område', 'ZoneMinMaxBlobs' => 'Min/Max Blobs', 'ZoneMinMaxFiltArea' => 'Min/Max Filtreret Område', 'ZoneMinMaxPixelThres' => 'Min/Max Pixel Grænseværdi (0-255)', + 'ZoneMinderLog' => 'ZoneMinder Log', 'ZoneOverloadFrames' => 'Antal Rammer At Ignorere Efter Overload', - 'ZoneExtendAlarmFrames' => 'Udvid Antal Alarm Rammer', 'Zones' => 'Zoner', - 'Zone' => 'Zone', + 'Zoom' => 'Zoom', 'ZoomIn' => 'Zoom Ind', 'ZoomOut' => 'Zoom Ud', - 'Zoom' => 'Zoom', ); // Complex replacements with formatting and/or placements, must be passed through sprintf diff --git a/web/lang/en_gb.php b/web/lang/en_gb.php index 9042fe839..a8569639c 100644 --- a/web/lang/en_gb.php +++ b/web/lang/en_gb.php @@ -32,7 +32,7 @@ // a formatting string. If the dynamic element is a number you will usually need to use a variable // replacement also as described below. // c) Variable replacements are used in conjunction with complex replacements and involve the generation -// of a singular or plural noun depending on the number passed into the zmVlang function. See the +// of a singular or plural noun depending on the number passed into the zmVlang function. See the // the zmVlang section below for a further description of this. // d) Optional strings which can be used to replace the prompts and/or help text for the Options section // of the web interface. These are not listed below as they are quite large and held in the database @@ -40,7 +40,7 @@ // quite easily from the Config table in the database if necessary. // 3. The tokens listed below are not used to build up phrases or sentences from single words. Therefore // you can safely assume that a single word token will only be used in that context. -// 4. In new language files, or if you are changing only a few words or phrases it makes sense from a +// 4. In new language files, or if you are changing only a few words or phrases it makes sense from a // maintenance point of view to include the original language file and override the old definitions rather // than copy all the language tokens across. To do this change the line below to whatever your base language // is and uncomment it. @@ -57,10 +57,10 @@ // If you do need to change your locale, be aware that the format of this function // is subtlely different in versions of PHP before and after 4.3.0, see // http://uk2.php.net/manual/en/function.setlocale.php for details. -// Also be aware that changing the whole locale may affect some floating point or decimal +// Also be aware that changing the whole locale may affect some floating point or decimal // arithmetic in the database, if this is the case change only the individual locale areas // that don't affect this rather than all at once. See the examples below. -// Finally, depending on your setup, PHP may not enjoy have multiple locales in a shared +// Finally, depending on your setup, PHP may not enjoy have multiple locales in a shared // threaded environment, if you get funny errors it may be this. // // Examples @@ -102,8 +102,11 @@ $SLANG = array( 'AlarmRGBUnset' => 'You must set an alarm RGB colour', 'Alert' => 'Alert', 'All' => 'All', + 'AllTokensRevoked' => 'All Tokens Revoked', 'AnalysisFPS' => 'Analysis FPS', 'AnalysisUpdateDelay' => 'Analysis Update Delay', + 'API' => 'API', + 'APIEnabled' => 'API Enabled', 'Apply' => 'Apply', 'ApplyingStateChange' => 'Applying State Change', 'ArchArchived' => 'Archived Only', @@ -129,6 +132,7 @@ $SLANG = array( 'AttrMaxScore' => 'Max. Score', 'AttrMonitorId' => 'Monitor Id', 'AttrMonitorName' => 'Monitor Name', + 'AttrSecondaryStorageArea' => 'Secondary Storage Area', 'AttrStorageArea' => 'Storage Area', 'AttrFilterServer' => 'Server Filter is Running On', 'AttrMonitorServer' => 'Server Monitor is Running On', @@ -173,6 +177,7 @@ $SLANG = array( 'BadPostEventCount' => 'Post event image count must be an integer of zero or more', 'BadPreEventCount' => 'Pre event image count must be at least zero, and less than image buffer size', 'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer', + 'BadNoSaveJPEGsOrVideoWriter' => 'SaveJPEGs and VideoWriter are both set to disabled. Nothing will be recorded!', 'BadSectionLength' => 'Section length must be an integer of 30 or more', 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', 'BadStreamReplayBuffer' => 'Stream replay buffer must be an integer of zero or more', @@ -216,6 +221,7 @@ $SLANG = array( 'CanMoveRel' => 'Can Move Relative', 'CanPan' => 'Can Pan' , 'CanReset' => 'Can Reset', + 'CanReboot' => 'Can Reboot', 'CanSetPresets' => 'Can Set Presets', 'CanSleep' => 'Can Sleep', 'CanTilt' => 'Can Tilt', @@ -237,6 +243,7 @@ $SLANG = array( 'Cause' => 'Cause', 'CheckMethod' => 'Alarm Check Method', 'ChooseDetectedCamera' => 'Choose Detected Camera', + 'ChooseDetectedProfile' => 'Choose Detected Profile', 'ChooseFilter' => 'Choose Filter', 'ChooseLogFormat' => 'Choose a log format', 'ChooseLogSelection' => 'Choose a log selection', @@ -270,6 +277,7 @@ $SLANG = array( 'Debug' => 'Debug', 'DefaultRate' => 'Default Rate', 'DefaultScale' => 'Default Scale', + 'DefaultCodec' => 'Default Method For Live View', 'DefaultView' => 'Default View', 'Deinterlacing' => 'Deinterlacing', 'RTSPDescribe' => 'Use RTSP Response Media URL', @@ -293,7 +301,7 @@ $SLANG = array( 'Display' => 'Display', 'Displaying' => 'Displaying', 'DonateAlready' => 'No, I\'ve already donated', - 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', + 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', 'Donate' => 'Please Donate', 'DonateRemindDay' => 'Not yet, remind again in 1 day', 'DonateRemindHour' => 'Not yet, remind again in 1 hour', @@ -323,7 +331,9 @@ $SLANG = array( 'Events' => 'Events', 'Exclude' => 'Exclude', 'Execute' => 'Execute', + 'ExportCompress' => 'Use Compression', 'ExportDetails' => 'Export Event Details', + 'ExportMatches' => 'Export Matches', 'Exif' => 'Embed EXIF data into image', 'Export' => 'Export', 'DownloadVideo' => 'Download Video', @@ -348,6 +358,7 @@ $SLANG = array( 'FilterArchiveEvents' => 'Archive all matches', 'FilterUpdateDiskSpace' => 'Update used disk space', 'FilterDeleteEvents' => 'Delete all matches', + 'FilterCopyEvents' => 'Copy all matches', 'FilterMoveEvents' => 'Move all matches', 'FilterEmailEvents' => 'Email details of all matches', 'FilterExecuteEvents' => 'Execute command on all matches', @@ -415,6 +426,7 @@ $SLANG = array( 'Images' => 'Images', 'Include' => 'Include', 'In' => 'In', + 'InvalidateTokens' => 'Invalidate all generated tokens', 'Inverted' => 'Inverted', 'Iris' => 'Iris', 'KeyString' => 'Key String', @@ -536,6 +548,7 @@ $SLANG = array( 'NewState' => 'New State', 'NewUser' => 'New User', 'Next' => 'Next', + 'NextMonitor' => 'Next Monitor', 'NoDetectedCameras' => 'No Detected Cameras', 'NoDetectedProfiles' => 'No Detected Profiles', 'NoFramesRecorded' => 'There are no frames recorded for this event', @@ -566,6 +579,8 @@ $SLANG = array( 'OpNotMatches' => 'does not match', 'OpIs' => 'is', 'OpIsNot' => 'is not', + 'OpLike' => 'contains', + 'OpNotLike' => 'does not contain', 'OptionalEncoderParam' => 'Optional Encoder Parameters', 'OptionHelp' => 'Option Help', 'OptionRestartWarning' => 'These changes may not come into effect fully\nwhile the system is running. When you have\nfinished making your changes please ensure that\nyou restart ZoneMinder.', @@ -581,16 +596,22 @@ $SLANG = array( 'PanRight' => 'Pan Right', 'PanTilt' => 'Pan/Tilt', 'Parameter' => 'Parameter', + 'ParentGroup' => 'Parent Group', 'Password' => 'Password', 'PasswordsDifferent' => 'The new and confirm passwords are different', + 'PathToIndex' => 'Path To Index', + 'PathToZMS' => 'Path To ZMS', + 'PathToApi' => 'Path To Api', 'Paths' => 'Paths', 'Pause' => 'Pause', + 'PauseCycle' => 'Pause Cycle', 'PhoneBW' => 'Phone B/W', 'Phone' => 'Phone', 'PixelDiff' => 'Pixel Diff', 'Pixels' => 'pixels', 'PlayAll' => 'Play All', 'Play' => 'Play', + 'PlayCycle' => 'Play Cycle', 'Plugins' => 'Plugins', 'PleaseWait' => 'Please Wait', 'Point' => 'Point', @@ -600,6 +621,19 @@ $SLANG = array( 'Preset' => 'Preset', 'Presets' => 'Presets', 'Prev' => 'Prev', + 'PreviousMonitor' => 'Previous Monitor', + 'Privacy' => 'Privacy', + 'PrivacyAbout' => 'About', + 'PrivacyAboutText' => 'Since 2002, ZoneMinder has been the premier free and open-source Video Management System (VMS) solution for Linux platforms. ZoneMinder is supported by the community and is managed by those who choose to volunteer their spare time to the project. The best way to improve ZoneMinder is to get involved.', + 'PrivacyContact' => 'Contact', + 'PrivacyContactText' => 'Please contact us here for any questions regarding our privacy policy or to have your information removed.

For support, there are three primary ways to engage with the community:

Our Github forum is only for bug reporting. Please use our user forum or slack channel for all other questions or comments.

', + 'PrivacyCookies' => 'Cookies', + 'PrivacyCookiesText' => 'Whether you use a web browser or a mobile app to communicate with the ZoneMinder server, a ZMSESSID cookie is created on the client to uniquely identify a session with the ZoneMinder server. ZmCSS and zmSkin cookies are created to remember your style and skin choices.', + 'PrivacyTelemetry' => 'Telemetry', + 'PrivacyTelemetryText' => 'Because ZoneMinder is open-source, anyone can install it without registering. This makes it difficult to answer questions such as: how many systems are out there, what is the largest system out there, what kind of systems are out there, or where are these systems located? Knowing the answers to these questions, helps users who ask us these questions, and it helps us set priorities based on the majority user base.', + 'PrivacyTelemetryList' => 'The ZoneMinder Telemetry daemon collects the following data about your system:
  • A unique identifier (UUID)
  • City based location is gathered by querying ipinfo.io. City, region, country, latitude, and longitude parameters are saved. The latitude and longitude coordinates are accurate down to the city or town level only!
  • Current time
  • Total number of monitors
  • Total number of events
  • System architecture
  • Operating system kernel, distro, and distro version
  • Version of ZoneMinder
  • Total amount of memory
  • Number of cpu cores
', + 'PrivacyMonitorList' => 'The following configuration parameters from each monitor are collected:
  • Id
  • Name
  • Type
  • Function
  • Width
  • Height
  • Colours
  • MaxFPS
  • AlarmMaxFPS
', + 'PrivacyConclusionText' => 'We are NOT collecting any image specific data from your cameras. We don’t know what your cameras are watching. This data will not be sold or used for any purpose not stated herein. By clicking accept, you agree to send us this data to help make ZoneMinder a better product. By clicking decline, you can still freely use ZoneMinder and all its features.', 'Probe' => 'Probe', 'ProfileProbe' => 'Stream Probe', 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', @@ -634,14 +668,19 @@ $SLANG = array( 'RestrictedMonitors' => 'Restricted Monitors', 'ReturnDelay' => 'Return Delay', 'ReturnLocation' => 'Return Location', + 'RevokeAllTokens' => 'Revoke All Tokens', 'Rewind' => 'Rewind', 'RotateLeft' => 'Rotate Left', 'RotateRight' => 'Rotate Right', 'RTSPTransport' => 'RTSP Transport Protocol', + 'RunAudit' => 'Run Audit Process', 'RunLocalUpdate' => 'Please run zmupdate.pl to update', 'RunMode' => 'Run Mode', 'Running' => 'Running', 'RunState' => 'Run State', + 'RunStats' => 'Run Stats Process', + 'RunTrigger' => 'Run Trigger Process', + 'RunEventNotification' => 'Run Event Notification Process', 'SaveAs' => 'Save as', 'SaveFilter' => 'Save Filter', 'SaveJPEGs' => 'Save JPEGs', @@ -661,7 +700,9 @@ $SLANG = array( 'Settings' => 'Settings', 'ShowFilterWindow' => 'Show Filter Window', 'ShowTimeline' => 'Show Timeline', + 'Shutdown' => 'Shutdown', 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', 'Size' => 'Size', 'SkinDescription' => 'Change the skin for this session', 'CSSDescription' => 'Change the css for this session', @@ -696,6 +737,8 @@ $SLANG = array( 'Stills' => 'Stills', 'Stopped' => 'Stopped', 'Stop' => 'Stop', + 'StorageArea' => 'Storage Area', + 'StorageDoDelete' => 'Do Deletes', 'StorageScheme' => 'Scheme', 'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Stream' => 'Stream', @@ -728,6 +771,7 @@ $SLANG = array( 'TurboPanSpeed' => 'Turbo Pan Speed', 'TurboTiltSpeed' => 'Turbo Tilt Speed', 'Type' => 'Type', + 'TZUnset' => 'Unset - use value in php.ini', 'Unarchive' => 'Unarchive', 'Undefined' => 'Undefined', 'Units' => 'Units', @@ -737,7 +781,7 @@ $SLANG = array( 'Update' => 'Update', 'Upload' => 'Upload', 'Updated' => 'Updated', - 'UsedPlugins' => 'Used Plugins', + 'UsedPlugins' => 'Used Plugins', 'UseFilterExprsPost' => ' filter expressions', // This is used at the end of the phrase 'use N filter expressions' 'UseFilterExprsPre' => 'Use ', // This is used at the beginning of the phrase 'use N filter expressions' 'UseFilter' => 'Use Filter', @@ -751,6 +795,7 @@ $SLANG = array( 'VersionRemindNever' => 'Don\'t remind about new versions', 'VersionRemindWeek' => 'Remind again in 1 week', 'Version' => 'Version', + 'ViewMatches' => 'View Matches', 'VideoFormat' => 'Video Format', 'VideoGenFailed' => 'Video Generation Failed!', 'VideoGenFiles' => 'Existing Video Files', @@ -815,7 +860,7 @@ $CLANG = array( 'VersionMismatch' => 'Version mismatch, system is version %1$s, database is %2$s.', ); -// The next section allows you to describe a series of word ending and counts used to +// The next section allows you to describe a series of word ending and counts used to // generate the correctly conjugated forms of words depending on a count that is associated // with that word. // This intended to allow phrases such a '0 potatoes', '1 potato', '2 potatoes' etc to @@ -856,7 +901,7 @@ $VLANG = array( // with variable counts. This is used to conjugate the Vlang arrays above with a number passed // in to generate the correct noun form. // -// In languages such as English this is fairly simple +// In languages such as English this is fairly simple // Note this still has to be used with printf etc to get the right formatting function zmVlang( $langVarArray, $count ) { @@ -874,9 +919,9 @@ function zmVlang( $langVarArray, $count ) // This is an version that could be used in the Russian example above // The rules are that the first word form is used if the count ends in // 0, 5-9 or 11-19. The second form is used then the count ends in 1 -// (not including 11 as above) and the third form is used when the +// (not including 11 as above) and the third form is used when the // count ends in 2-4, again excluding any values ending in 12-14. -// +// // function zmVlang( $langVarArray, $count ) // { // $secondlastdigit = substr( $count, -2, 1 ); @@ -884,7 +929,7 @@ function zmVlang( $langVarArray, $count ) // // or // // $secondlastdigit = ($count/10)%10; // // $lastdigit = $count%10; -// +// // // Get rid of the special cases first, the teens // if ( $secondlastdigit == 1 && $lastdigit != 0 ) // { @@ -918,7 +963,7 @@ function zmVlang( $langVarArray, $count ) // die( 'Error, unable to correlate variable language string' ); // } -// This is an example of how the function is used in the code which you can uncomment and +// This is an example of how the function is used in the code which you can uncomment and // use to test your custom function. //$monitors = array(); //$monitors[] = 1; // Choose any number @@ -929,45 +974,75 @@ function zmVlang( $langVarArray, $count ) // So for example, to override the help text for ZM_LANG_DEFAULT do $OLANG = array( 'OPTIONS_FFMPEG' => array( - 'Help' => "Parameters in this field are passed on to FFmpeg. Multiple parameters can be separated by ,~~ ". - "Examples (do not enter quotes)~~~~". - "\"allowed_media_types=video\" Set datatype to request fromcam (audio, video, data)~~~~". - "\"reorder_queue_size=nnn\" Set number of packets to buffer for handling of reordered packets~~~~". - "\"loglevel=debug\" Set verbosity of FFmpeg (quiet, panic, fatal, error, warning, info, verbose, debug)" + 'Help' => ' + Parameters in this field are passed on to FFmpeg. Multiple parameters can be separated by ,~~ + Examples (do not enter quotes)~~~~ + "allowed_media_types=video" Set datatype to request fromcam (audio, video, data)~~~~ + "reorder_queue_size=nnn" Set number of packets to buffer for handling of reordered packets~~~~ + "loglevel=debug" Set verbosity of FFmpeg (quiet, panic, fatal, error, warning, info, verbose, debug) + ' ), - 'OPTIONS_RTSPTrans' => array( - 'Help' => "This sets the RTSP Transport Protocol for FFmpeg.~~ ". - "TCP - Use TCP (interleaving within the RTSP control channel) as transport protocol.~~". - "UDP - Use UDP as transport protocol. Higher resolution cameras have experienced some 'smearing' while using UDP, if so try TCP~~". - "UDP Multicast - Use UDP Multicast as transport protocol~~". - "HTTP - Use HTTP tunneling as transport protocol, which is useful for passing proxies.~~" + 'OPTIONS_DECODERHWACCELNAME' => array( + 'Help' => ' + This is equivalent to the ffmpeg -hwaccel command line option. With intel graphics support, use "vaapi". For NVIDIA cuda support use "cuda". To check for support, run ffmpeg -hwaccels on the command line.' + ), + 'OPTIONS_DECODERHWACCELDEVICE' => array( + 'Help' => ' + This is equivalent to the ffmpeg -hwaccel_device command line option. You should only have to specify this if you have multiple GPUs. A typical value for Intel VAAPI would be /dev/dri/renderD128.' + ), + 'OPTIONS_RTSPTrans' => array( + 'Help' => ' + This sets the RTSP Transport Protocol for FFmpeg.~~ + TCP - Use TCP (interleaving within the RTSP control channel) as transport protocol.~~ + UDP - Use UDP as transport protocol. Higher resolution cameras have experienced some \'smearing\' while using UDP, if so try TCP~~ + UDP Multicast - Use UDP Multicast as transport protocol~~ + HTTP - Use HTTP tunneling as transport protocol, which is useful for passing proxies.~~ + ' ), 'OPTIONS_LIBVLC' => array( - 'Help' => "Parameters in this field are passed on to libVLC. Multiple parameters can be separated by ,~~ ". - "Examples (do not enter quotes)~~~~". - "\"--rtp-client-port=nnn\" Set local port to use for rtp data~~~~". - "\"--verbose=2\" Set verbosity of libVLC" + 'Help' => ' + Parameters in this field are passed on to libVLC. Multiple parameters can be separated by ,~~ + Examples (do not enter quotes)~~~~ + "--rtp-client-port=nnn" Set local port to use for rtp data~~~~ + "--verbose=2" Set verbosity of libVLC + ' ), 'OPTIONS_EXIF' => array( - 'Help' => "Enable this option to embed EXIF data into each jpeg frame." + 'Help' => 'Enable this option to embed EXIF data into each jpeg frame.' ), 'OPTIONS_RTSPDESCRIBE' => array( - 'Help' => "Sometimes, during the initial RTSP handshake, the camera will send an updated media URL. ". - "Enable this option to tell ZoneMinder to use this URL. Disable this option to ignore the ". - "value from the camera and use the value as entered in the monitor configuration~~~~". - "Generally this should be enabled. However, there are cases where the camera can get its". - "own URL incorrect, such as when the camera is streaming through a firewall"), + 'Help' => ' + Sometimes, during the initial RTSP handshake, the camera will send an updated media URL. + Enable this option to tell ZoneMinder to use this URL. Disable this option to ignore the + value from the camera and use the value as entered in the monitor configuration~~~~ + Generally this should be enabled. However, there are cases where the camera can get its + own URL incorrect, such as when the camera is streaming through a firewall + ' + ), 'OPTIONS_MAXFPS' => array( - 'Help' => "This field has certain limitations when used for non-local devices.~~ ". - "Failure to adhere to these limitations will cause a delay in live video, irregular frame skipping, ". - "and missed events~~". - "For streaming IP cameras, do not use this field to reduce the frame rate. Set the frame rate in the". - " camera, instead. You can, however, use a value that is slightly higher than the frame rate in the camera. ". - "In this case, this helps keep the cpu from being overtaxed in the event of a network problem.~~". - "Some, mostly older, IP cameras support snapshot mode. In this case ZoneMinder is actively polling the camera ". - "for new images. In this case, it is safe to use the field." + 'Help' => ' + This field has certain limitations when used for non-local devices.~~ + Failure to adhere to these limitations will cause a delay in live video, irregular frame skipping, + and missed events~~ + For streaming IP cameras, do not use this field to reduce the frame rate. Set the frame rate in the + camera, instead. In the past it was advised to set a value higher than the frame rate of the camera + but this is no longer needed or a good idea. + Some, mostly older, IP cameras support snapshot mode. In this case ZoneMinder is actively polling the camera + for new images. In this case, it is safe to use the field. + ' ), - + 'OPTIONS_LINKED_MONITORS' => array( + 'Help' => ' + 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. Click on ‘Select’ to choose linked monitors. + Be very careful not to create circular dependencies with this feature + because you will have infinitely persisting alarms which is almost + certainly not what you want! To unlink monitors you can ctrl-click. + ' + ), + // 'LANG_DEFAULT' => array( // 'Prompt' => "This is a new prompt for this option", // 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked" diff --git a/web/lang/es_ar.php b/web/lang/es_ar.php index cbb3c2d88..a8da807be 100644 --- a/web/lang/es_ar.php +++ b/web/lang/es_ar.php @@ -29,6 +29,8 @@ $SLANG = array( 'Actual' => 'Actual', 'AddNewControl' => 'Add New Control', 'AddNewMonitor' => 'Agregar Nuevo Monitor', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Agregar Nuevo Usuario', 'AddNewZone' => 'Agregar Nueva Zona', 'Alarm' => 'Alarma', @@ -56,22 +58,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Estado Archivo', 'AttrAvgScore' => 'Puntaje Prom.', 'AttrCause' => 'Cause', - 'AttrDate' => 'Fecha', - 'AttrDateTime' => 'Fecha/Hora', 'AttrDiskBlocks' => 'Disk Blocks', 'AttrDiskPercent' => 'Disk Percent', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Duración', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Cuadros', 'AttrId' => 'Id', 'AttrMaxScore' => 'Puntaje Máximo', 'AttrMonitorId' => 'Monitor Id', 'AttrMonitorName' => 'Nombre Monitor', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Name', 'AttrNotes' => 'Notes', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'System Load', - 'AttrTime' => 'Hora', 'AttrTotalScore' => 'Puntaje Total', - 'AttrWeekday' => 'Día Semana', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Auto Stop Timeout', 'Available' => 'Available', // Added - 2009-03-31 @@ -104,9 +116,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer', 'BadSectionLength' => 'Section length must be an integer of 30 or more', 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Stream replay buffer must be an integer of zero or more', 'BadWarmupCount' => 'Warmup frames must be an integer of zero or more', 'BadWebColour' => 'Web colour must be a valid web colour string', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Width must be set to a valid value', 'Bandwidth' => 'Velocidad', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -142,6 +156,7 @@ $SLANG = array( 'CanMoveRel' => 'Can Move Relative', 'CanPan' => 'Can Pan' , 'CanReset' => 'Can Reset', + 'CanReboot' => 'Can Reboot', 'CanSetPresets' => 'Can Set Presets', 'CanSleep' => 'Can Sleep', 'CanTilt' => 'Can Tilt', @@ -170,10 +185,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Choose Preset', 'Clear' => 'Clear', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Cerrar', 'Colour' => 'Color', 'Command' => 'Command', 'Component' => 'Component', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Config.', 'ConfiguredFor' => 'Configurado Para', 'ConfirmDeleteEvents' => 'Are you sure you wish to delete the selected events?', @@ -223,7 +240,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'Please Donate', 'DonateAlready' => 'No, I\'ve already donated', - 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', + 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', 'DonateRemindDay' => 'Not yet, remind again in 1 day', 'DonateRemindHour' => 'Not yet, remind again in 1 hour', 'DonateRemindMonth' => 'Not yet, remind again in 1 month', @@ -231,9 +248,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Not yet, remind again in 1 week', 'DonateYes' => 'Yes, I\'d like to donate now', 'Download' => 'Download', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 'Duration' => 'Duración', 'Edit' => 'Editar', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Enable Alarms', 'Enabled' => 'Habilitado', @@ -250,6 +269,7 @@ $SLANG = array( 'Events' => 'Eventos', 'Exclude' => 'Excluir', 'Execute' => 'Execute', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Export', 'ExportDetails' => 'Export Event Details', 'ExportFailed' => 'Export Failed', @@ -279,8 +299,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Ejecutar un comando en las coincidencias', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Mandar un mensaje de los eventos', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filtro Px', 'FilterUnset' => 'You must specify a filter width and height', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Subir los eventos que coincidan', 'FilterVideoEvents' => 'Create video for all matches', 'Filters' => 'Filters', @@ -305,6 +327,7 @@ $SLANG = array( 'Function' => 'Función', 'Gain' => 'Gain', 'General' => 'General', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Crear Video', 'GeneratingVideo' => 'Creando Video', 'GoToZoneMinder' => 'Ir a Zoneminder.com', @@ -325,6 +348,7 @@ $SLANG = array( 'High' => 'Alta', 'HighBW' => 'Alta B/W', 'Home' => 'Home', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Hora', 'Hue' => 'Saturación', 'Id' => 'Id', @@ -349,6 +373,7 @@ $SLANG = array( 'Line' => 'Line', // Added - 2011-06-16 'LinkedMonitors' => 'Linked Monitors', 'List' => 'List', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Carga', 'Local' => 'Local', 'Log' => 'Log', // Added - 2011-06-16 @@ -435,6 +460,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 'Monitors' => 'Monitores', 'Montage' => 'Cámara Múltiple', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Mes', 'More' => 'More', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -461,6 +487,7 @@ $SLANG = array( 'Next' => 'Siguiente', 'No' => 'No', 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'No hay movimientos grabados para este evento', 'NoGroup' => 'No Group', 'NoSavedFilters' => 'FiltrosNoGuardados', @@ -479,6 +506,8 @@ $SLANG = array( 'OpGt' => 'mayor que', 'OpGtEq' => 'mayor o igual que', 'OpIn' => 'En sistema', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'menor que', 'OpLtEq' => 'menor o igual que', 'OpMatches' => 'Coincide', @@ -488,6 +517,7 @@ $SLANG = array( 'Open' => 'Open', 'OptionHelp' => 'Ayuda', 'OptionRestartWarning' => 'Estos cambios no se guardaran completamente\nmientras el sistema se ejecute. Cuando termine\nde realizar los cambios asegurese de\nreiniciar Zoneminder.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Opciones', 'OrEnterNewName' => 'o agregue nombre', 'Order' => 'Order', @@ -525,9 +555,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Ritmo', 'Real' => 'Real', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Registro', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Reference Image Blend %ge', 'Refresh' => 'Actualizar', 'Remote' => 'Remote', @@ -543,6 +577,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Reset', 'ResetEventCounts' => 'Borrar Contador Eventos', 'Restart' => 'Reiniciar', @@ -561,6 +596,7 @@ $SLANG = array( 'Save' => 'Guardar', 'SaveAs' => 'Guardar Como', 'SaveFilter' => 'Guardar Filtro', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Escala', 'Score' => 'Res.', 'Secs' => 'Seg', @@ -577,6 +613,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Abrir Filtro', 'ShowTimeline' => 'Show Timeline', 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Size', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 'Sleep' => 'Sleep', @@ -596,6 +633,10 @@ $SLANG = array( 'State' => 'Estado', 'Stats' => 'Est.', 'Status' => 'Estado', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Step', 'StepBack' => 'Step Back', 'StepForward' => 'Step Forward', @@ -606,6 +647,8 @@ $SLANG = array( 'Stills' => 'Fotos', 'Stop' => 'Desactivar', 'Stopped' => 'Apagado', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Stream', 'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Submit' => 'Submit', @@ -625,6 +668,7 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => 'Etiqueta Hora', 'TimestampLabelFormat' => 'Formato Etiqueta Hora', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'Eje X Etiqueta Hora', 'TimestampLabelY' => 'Eje Y Etiqueta Hora', 'Today' => 'Today', @@ -671,6 +715,7 @@ $SLANG = array( 'VideoGenParms' => 'Parametros Generacion Video', 'VideoGenSucceeded' => 'Video Generation Succeeded!', 'VideoSize' => 'Tamaño Video', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Ver', 'ViewAll' => 'Ver Todo', 'ViewEvent' => 'View Event', @@ -680,6 +725,7 @@ $SLANG = array( 'Watch' => 'Monitor', 'Web' => 'Web', 'WebColour' => 'Web Colour', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Semana', 'White' => 'White', 'WhiteBalance' => 'White Balance', diff --git a/web/lang/es_es.php b/web/lang/es_es.php index 3e27d0fae..f97ea0ff1 100644 --- a/web/lang/es_es.php +++ b/web/lang/es_es.php @@ -78,6 +78,8 @@ $SLANG = array( 'Actual' => 'Actual', 'AddNewControl' => 'Añadir nuevo control', 'AddNewMonitor' => 'Añadir nuevo monitor', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Añadir nuevo usuario', 'AddNewZone' => 'Añadir nueva zona', 'Alarm' => 'Alarma', @@ -105,22 +107,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Estado de archivo', 'AttrAvgScore' => 'Promed. señal', 'AttrCause' => 'Causa', - 'AttrDate' => 'Fecha', - 'AttrDateTime' => 'Fecha/Hora', 'AttrDiskBlocks' => 'Bloques del disco', 'AttrDiskPercent' => 'Porcentaje del disco', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Duración', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Marcos', 'AttrId' => 'Id', 'AttrMaxScore' => 'Señal máxima', 'AttrMonitorId' => 'Id monitor', 'AttrMonitorName' => 'Nombre del monitor', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Nombre', 'AttrNotes' => 'Notas', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'Carga del sistema', - 'AttrTime' => 'Hora', 'AttrTotalScore' => 'Señal total', - 'AttrWeekday' => 'Día de la semana', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Autodetener tiempo de espera', 'Available' => 'Disponible', @@ -153,9 +165,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'El porcentaje de la referencia de mezcla debe ser un entero positivo', 'BadSectionLength' => 'La duración de la sección debe ser un entero de 30 o más', 'BadSignalCheckColour' => 'El color de verificación de señal debe ser una cadena de color RGB válida', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer' => 'La secuencia de búfer de reproducción debe ser un entero de cero o más', 'BadWarmupCount' => 'Los marcos de calentamiento deben ser un entero de cero o más', 'BadWebColour' => 'El color web debe ser una cadena de color web válida', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'El ancho debe tener un valor válido', 'Bandwidth' => 'Ancho de banda', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -191,6 +205,7 @@ $SLANG = array( 'CanMoveRel' => 'Puede moverse de forma relativa', 'CanPan' => 'Puede desplazarse' , 'CanReset' => 'Puede restablecerse', + 'CanReboot' => 'Can Reboot', 'CanSetPresets' => 'Puede fefinir programaciones', 'CanSleep' => 'Puede dormirse', 'CanTilt' => 'Puede inclinarse', @@ -219,10 +234,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Elegir selección de registro', 'ChoosePreset' => 'Elegir preprogramación', 'Clear' => 'Limpiar', + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Cerrar', 'Colour' => 'Color', 'Command' => 'Comando', 'Component' => 'Componente', + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Config', 'ConfiguredFor' => 'Configurado para', 'ConfirmDeleteEvents' => '¿Seguro que desea borrar los eventos seleccionados?', @@ -272,7 +289,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18 'Donate' => 'Por favor, done', 'DonateAlready' => 'No, ya he donado', - 'DonateEnticement' => 'Ha estado ejecutando ZoneMinder por un tiempo y con suerte le resultará un útil complemento para su seguridad en hogar y trabajo. Aunque ZoneMinder es, y será, libre y de código abierto, cuesta dinero desarrollarlo y mantenerlo. Si quiere ayudar a mantener un futuro desarrollo y nuevas funciones entonces considere hacer un donativo por favor. Donar es, por supuesto, opcional pero muy apreciado y puede donar tanto como desee sin importar la cantidad.

Si desea hacer una donación por favor seleccione la opción de debajo o vaya a http://www.zoneminder.com/donate.html en su navegador.

Muchas gracias por usar ZoneMinder y no se olvide de vistar los foros en ZoneMinder.com para obtener soporte o hacer sugerencias sobre cómo mejorar su experiencia con ZoneMinder aún más.', + 'DonateEnticement' => 'Ha estado ejecutando ZoneMinder por un tiempo y con suerte le resultará un útil complemento para su seguridad en hogar y trabajo. Aunque ZoneMinder es, y será, libre y de código abierto, cuesta dinero desarrollarlo y mantenerlo. Si quiere ayudar a mantener un futuro desarrollo y nuevas funciones entonces considere hacer un donativo por favor. Donar es, por supuesto, opcional pero muy apreciado y puede donar tanto como desee sin importar la cantidad.

Si desea hacer una donación por favor seleccione la opción de debajo o vaya a https://zoneminder.com/donate/ en su navegador.

Muchas gracias por usar ZoneMinder y no se olvide de vistar los foros en ZoneMinder.com para obtener soporte o hacer sugerencias sobre cómo mejorar su experiencia con ZoneMinder aún más.', 'DonateRemindDay' => 'Aún no, recordarme de nuevo en 1 día', 'DonateRemindHour' => 'Aún no, recordarme de nuevo en 1 hora', 'DonateRemindMonth' => 'Aún no, recordarme de nuevo en 1 mes', @@ -280,9 +297,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Aún no, recordarme de nuevo en 1 semana', 'DonateYes' => 'Sí, me gustaría hacer una donación ahora', 'Download' => 'Descargar', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicar nombre de monitor', 'Duration' => 'Duración', 'Edit' => 'Editar', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Habilitar alarmas', 'Enabled' => 'Habilitado', @@ -299,6 +318,7 @@ $SLANG = array( 'Events' => 'Eventos', 'Exclude' => 'Excluir', 'Execute' => 'Ejecutar', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Exportar', 'ExportDetails' => 'Exportar detalles de evento', 'ExportFailed' => 'Fallo al exportar', @@ -328,8 +348,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Ejecutar comando para todas las coincidencias', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Detalles de mensaje de todas las coincidencias', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filtrar Px', 'FilterUnset' => 'Debe especificar un ancho y un alto para el filtro', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Subir todas las coincidencias', 'FilterVideoEvents' => 'Create video for all matches', // Added - 2011-08-23 'Filters' => 'Filtros', @@ -354,6 +376,7 @@ $SLANG = array( 'Function' => 'Función', 'Gain' => 'Ganancia', 'General' => 'General', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Generate Video', // Added - 2011-08-23 'GeneratingVideo' => 'Generating Video', // Added - 2011-08-23 'GoToZoneMinder' => 'Ir a ZoneMinder.com', @@ -374,6 +397,7 @@ $SLANG = array( 'High' => 'Alto', 'HighBW' => 'Alto B/B', 'Home' => 'Inicio', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Hora', 'Hue' => 'Matiz', 'Id' => 'Id', @@ -398,6 +422,7 @@ $SLANG = array( 'Line' => 'Línea', 'LinkedMonitors' => 'Monitores enlazados', 'List' => 'Lista', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Carga', 'Local' => 'Local', 'Log' => 'Registro', @@ -484,6 +509,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'La lista de debajo muestra las cámaras analógicas y en red detectadas y si ya están siendo usadas o están disponibles para seleccionar.

Seleccione la entrada deseada de la lista de debajo.

Por favor tenga en cuenta que podrían no detectarse todas las cámaras y que elegir una cámara aquí podría sobrescribir cualquier valor que ya hubiera configurado para el monitor actual.

', 'Monitors' => 'Monitores', 'Montage' => 'Montaje', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Mes', 'More' => 'Más', 'MotionFrameSkip' => 'Motion Frame Skip', @@ -510,6 +536,7 @@ $SLANG = array( 'Next' => 'Siguiente', 'No' => 'No', 'NoDetectedCameras' => 'No se detectaron cámaras', + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'No hay marcos grabados para este evento', 'NoGroup' => 'Sin grupo', 'NoSavedFilters' => 'No hay filtros guardados', @@ -528,6 +555,8 @@ $SLANG = array( 'OpGt' => 'mayor que', 'OpGtEq' => 'mayor que o igual a', 'OpIn' => 'en conjunto', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'menor que', 'OpLtEq' => 'menor que o igual a', 'OpMatches' => 'coincidencias', @@ -537,6 +566,7 @@ $SLANG = array( 'Open' => 'Abrir', 'OptionHelp' => 'Ayuda de la opción', 'OptionRestartWarning' => 'Estos cambios podrían no surtir un efecto completo mientras el sistema esté ejecutándose. Cuando haya terminado haciendo cambios por favor asegúrese de reiniciar ZoneMinder.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Opciones', 'OrEnterNewName' => 'o introduzca un nuevo nombre', 'Order' => 'Orden', @@ -574,9 +604,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocolo', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Valorar', 'Real' => 'Real', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Grabar', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Referencia de mezcla de imagen %ge', 'Refresh' => 'Refrescar', 'Remote' => 'Remoto', @@ -592,6 +626,7 @@ $SLANG = array( 'ReplayAll' => 'Todos los eventos', 'ReplayGapless' => 'Eventos sin espacios', 'ReplaySingle' => 'Evento individual', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Restablecer', 'ResetEventCounts' => 'Restablecer número de eventos', 'Restart' => 'Reiniciar', @@ -610,6 +645,7 @@ $SLANG = array( 'Save' => 'Guardar', 'SaveAs' => 'Guardar cómo', 'SaveFilter' => 'Guardar filtro', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Escalar', 'Score' => 'Cuenta', 'Secs' => 'Segs', @@ -626,6 +662,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Mostrar ventana de filtros', 'ShowTimeline' => 'Mostrar línea de tiempo', 'SignalCheckColour' => 'Color de comprobación de señal', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Tamaño', 'SkinDescription' => 'Cambiar el tema por defecto para este ordenador', 'Sleep' => 'Dormir', @@ -645,6 +682,10 @@ $SLANG = array( 'State' => 'Estado', 'Stats' => 'Estadísticas', 'Status' => 'Estado', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Salto', 'StepBack' => 'Salto atrás', 'StepForward' => 'Salto adelante', @@ -655,6 +696,8 @@ $SLANG = array( 'Stills' => 'Fijas', 'Stop' => 'Detener', 'Stopped' => 'Detenido', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Corriente', 'StreamReplayBuffer' => 'Secuencia de búfer de reproducción', 'Submit' => 'Enviar', @@ -674,9 +717,9 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => 'Marca de tiempo', 'TimestampLabelFormat' => 'Formato de hora multinacional', + 'TimestampLabelSize' => 'Tamaño de fuente', 'TimestampLabelX' => 'Etiqueta de tiempo X', 'TimestampLabelY' => 'Etiqueta de tiempo Y', - 'TimestampLabelSize' => 'Tamaño de fuente', 'Today' => 'Hoy', 'Tools' => 'Herramientas', 'Total' => 'Total', @@ -721,6 +764,7 @@ $SLANG = array( 'VideoGenParms' => 'Video Generation Parameters', // Added - 2011-08-23 'VideoGenSucceeded' => 'Video Generation Succeeded!', // Added - 2011-08-23 'VideoSize' => 'Video Size', // Added - 2011-08-23 + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Ver', 'ViewAll' => 'Ver todos', 'ViewEvent' => 'Ver evento', @@ -730,6 +774,7 @@ $SLANG = array( 'Watch' => 'Observar', 'Web' => 'Web', 'WebColour' => 'Color web', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Semana', 'White' => 'Blanco', 'WhiteBalance' => 'Balance de blancos', diff --git a/web/lang/et_ee.php b/web/lang/et_ee.php index 250728550..99a277144 100644 --- a/web/lang/et_ee.php +++ b/web/lang/et_ee.php @@ -85,6 +85,8 @@ $SLANG = array( 'Actual' => 'Aktuaalne', 'AddNewControl' => 'Lisa uus Kontroll', 'AddNewMonitor' => 'Lisa uus Monitor', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Lisa uus Kasutaja', 'AddNewZone' => 'Lisa uus Tsoon', 'Alarm' => 'Alarm', @@ -112,22 +114,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Arhiivi Staatus', 'AttrAvgScore' => 'Keskm. Skoor', 'AttrCause' => 'Põhjus', - 'AttrDate' => 'Kp.', - 'AttrDateTime' => 'Kp/Kellaaeg', 'AttrDiskBlocks' => 'Ketta Blokk', 'AttrDiskPercent' => 'Ketta Protsent', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Kestvus', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Kaadrid', 'AttrId' => 'Id', 'AttrMaxScore' => 'Maks. Skoor', 'AttrMonitorId' => 'Monitori Id', 'AttrMonitorName' => 'Monitori Nimi', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Nimi', 'AttrNotes' => 'Märkmed', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'Süsteemi Koormus', - 'AttrTime' => 'Kellaaeg', 'AttrTotalScore' => 'Skoor Kokku', - 'AttrWeekday' => 'Tööpäevad', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Auto Stop Ajalimiit', 'Available' => 'Saadaval', @@ -160,9 +172,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer', 'BadSectionLength' => 'Section length must be an integer of 30 or more', 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer' => 'Stream replay buffer must be an integer of zero or more', 'BadWarmupCount' => 'Warmup frames must be an integer of zero or more', 'BadWebColour' => 'Web colour must be a valid web colour string', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Width must be set to a valid value', 'Bandwidth' => 'Ribalaius', 'BandwidthHead' => 'Ribalaius', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -198,6 +212,7 @@ $SLANG = array( 'CanMoveRel' => 'Can Move Relative', 'CanPan' => 'Can Pan' , 'CanReset' => 'Can Reset', + 'CanReboot' => 'Can Reboot', 'CanSetPresets' => 'Can Set Presets', 'CanSleep' => 'Can Sleep', 'CanTilt' => 'Can Tilt', @@ -226,10 +241,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Choose Preset', 'Clear' => 'Clear', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Sule', 'Colour' => 'Värv', 'Command' => 'Käsk', 'Component' => 'Komponent', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Seadistus', 'ConfiguredFor' => 'Seadistatud', 'ConfirmDeleteEvents' => 'Oled sa kindel kustamaks valitud sündmused?', @@ -279,7 +296,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18 'Donate' => 'Palun Anneta', 'DonateAlready' => 'EI, Ma olen juba annetanud', - 'DonateEnticement' => 'Sa oled juba kasutanud ZoneMinderit juba mõnda aega. Nüüd kus sa oled leidnud, et see on kasulik lisa sinu kodule või sinu töökohale. Kuigi ZoneMinder on, jääb alatiseks, vabaks ja avatud lähtekoodiks, siiski selle arendamiseks kulub aega ja raha. Kui sa soovid meid aidata, siis toeta meid tuleviku arendusteks ja uute lisade loomiseks. Palun mõelge annetuse peale. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', + 'DonateEnticement' => 'Sa oled juba kasutanud ZoneMinderit juba mõnda aega. Nüüd kus sa oled leidnud, et see on kasulik lisa sinu kodule või sinu töökohale. Kuigi ZoneMinder on, jääb alatiseks, vabaks ja avatud lähtekoodiks, siiski selle arendamiseks kulub aega ja raha. Kui sa soovid meid aidata, siis toeta meid tuleviku arendusteks ja uute lisade loomiseks. Palun mõelge annetuse peale. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', 'DonateRemindDay' => 'Ei veel, tuleta meelde ühe päeva pärast', 'DonateRemindHour' => 'Ei veel, tuleta meelde ühe tunni pärast', 'DonateRemindMonth' => 'Ei veel, tuleta meelde ühe kuu pärast', @@ -287,9 +304,11 @@ $SLANG = array( 'DonateRemindWeek' => 'EI veel, tuleta meelde nädala pärast', 'DonateYes' => 'Jah, Ma soovin annetada', 'Download' => 'Lae alla', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Dubleeri Monitori Nimi', 'Duration' => 'Kestvus', 'Edit' => 'Muuda', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Luba Alarmid', 'Enabled' => 'Lubatud', @@ -306,6 +325,7 @@ $SLANG = array( 'Events' => 'Sündmuseid', 'Exclude' => 'Jäta välja', 'Execute' => 'Käivita', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Eksport', 'ExportDetails' => 'Ekspordi Sündmuste Detailid', 'ExportFailed' => 'Eksportimine Ebaõnnestus', @@ -335,8 +355,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Execute command on all matches', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Message details of all matches', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filter Px', 'FilterUnset' => 'You must specify a filter width and height', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Upload all matches', 'FilterVideoEvents' => 'Create video for all matches', 'Filters' => 'Filtrid', @@ -361,6 +383,7 @@ $SLANG = array( 'Function' => 'Funktsioon', 'Gain' => 'Gain', 'General' => 'Peamine', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Genereeri Video', 'GeneratingVideo' => 'Genereerin Videot', 'GoToZoneMinder' => 'Mine ZoneMinder.com', @@ -381,6 +404,7 @@ $SLANG = array( 'High' => 'Suurim', 'HighBW' => 'High B/W', 'Home' => 'Koju', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Tunnis', 'Hue' => 'Hue', 'Id' => 'Id', @@ -405,6 +429,7 @@ $SLANG = array( 'Line' => 'Line', // Added - 2011-06-16 'LinkedMonitors' => 'Lingitud monitorid', 'List' => 'List', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Koormus', 'Local' => 'Local', 'Log' => 'Logi', // Added - 2011-06-16 @@ -491,6 +516,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', 'Monitors' => 'Monitors', 'Montage' => 'Montage', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Kuus', 'More' => 'Veel', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -517,6 +543,7 @@ $SLANG = array( 'Next' => 'Järgmine', 'No' => 'Ei', 'NoDetectedCameras' => 'Ei leidnud kaameraid', + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Ei ole kaadreid salvetatud selles sündmuses', 'NoGroup' => 'Ei krupp', 'NoSavedFilters' => 'EiSalvestatudFiltreid', @@ -535,6 +562,8 @@ $SLANG = array( 'OpGt' => 'Suurem kui', 'OpGtEq' => 'suurem kui või võrdne', 'OpIn' => 'in set', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'vähem kui', 'OpLtEq' => 'vähem kui või võrdne', 'OpMatches' => 'klapib', @@ -544,6 +573,7 @@ $SLANG = array( 'Open' => 'Ava', 'OptionHelp' => 'Valik Aita', 'OptionRestartWarning' => 'These changes may not come into effect fully\nwhile the system is running. When you have\nfinished making your changes please ensure that\nyou restart ZoneMinder.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Seaded', 'OrEnterNewName' => 'või sisesta uus nimi', 'Order' => 'Järjekord', @@ -581,9 +611,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Rate', 'Real' => 'Reaaalne', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Salvesta', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Reference Image Blend %ge', 'Refresh' => 'Värskenda', 'Remote' => 'Remote', @@ -599,6 +633,7 @@ $SLANG = array( 'ReplayAll' => 'Kõik sündmused', 'ReplayGapless' => 'Lünkadeta sündmused', 'ReplaySingle' => 'Üksik sündmus', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Reset', 'ResetEventCounts' => 'Reset Event Counts', 'Restart' => 'Taaskäivita', @@ -617,6 +652,7 @@ $SLANG = array( 'Save' => 'Salvesta', 'SaveAs' => 'Salvesta kui', 'SaveFilter' => 'Salvesta Filter', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Skaala', 'Score' => 'Skoor', 'Secs' => 'Secs', @@ -633,6 +669,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Näita Filtri Akent', 'ShowTimeline' => 'Näita Timeline', 'SignalCheckColour' => 'Signaali Kontroll Värv', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Suurus', 'SkinDescription' => 'Vaheta veebilehe välimus selles arvutis', // Added - 2011-03-02 'Sleep' => 'Maga', @@ -652,6 +689,10 @@ $SLANG = array( 'State' => 'Olek', 'Stats' => 'Statistika', 'Status' => 'Staatus', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Samm', 'StepBack' => 'Samm tagasi', 'StepForward' => 'Samm edasi', @@ -662,6 +703,8 @@ $SLANG = array( 'Stills' => 'Stills', 'Stop' => 'Stop', 'Stopped' => 'Stopitud', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Striim', 'StreamReplayBuffer' => 'Striimi Replay Pildi Puhver', 'Submit' => 'Submit', @@ -681,6 +724,7 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => 'Timestamp', 'TimestampLabelFormat' => 'Timestamp Label Format', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'Timestamp Label X', 'TimestampLabelY' => 'Timestamp Label Y', 'Today' => 'Täna', @@ -727,6 +771,7 @@ $SLANG = array( 'VideoGenParms' => 'Video Genereerimise Parameetrid', 'VideoGenSucceeded' => 'Video Genereerimine Õnnestus!!!', 'VideoSize' => 'Video Suurus', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Vaata', 'ViewAll' => 'View All', 'ViewEvent' => 'Vaata Sündmust', @@ -736,6 +781,7 @@ $SLANG = array( 'Watch' => 'Vaata', 'Web' => 'Veeb', 'WebColour' => 'Veebi värv', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Nädalas', 'White' => 'White', 'WhiteBalance' => 'White Balance', diff --git a/web/lang/fr_fr.php b/web/lang/fr_fr.php index b140886fe..f379b04cd 100644 --- a/web/lang/fr_fr.php +++ b/web/lang/fr_fr.php @@ -84,6 +84,8 @@ $SLANG = array( 'Actual' => 'Réel', 'AddNewControl' => 'Ajouter contrôle', 'AddNewMonitor' => 'Ajouter caméra', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Ajouter utilisateur', 'AddNewZone' => 'Ajouter zone', 'Alarm' => 'Alarme', @@ -111,22 +113,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Etat Archive', 'AttrAvgScore' => 'Score moy.', 'AttrCause' => 'Cause', - 'AttrDate' => 'Date', - 'AttrDateTime' => 'Date/Heure', 'AttrDiskBlocks' => 'Blocs disque', 'AttrDiskPercent' => '% disque', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Durée', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Images', 'AttrId' => 'Id', 'AttrMaxScore' => 'Score max.', 'AttrMonitorId' => 'N°', 'AttrMonitorName' => 'Nom caméra', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Nom', 'AttrNotes' => 'Notes', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'Charge système', - 'AttrTime' => 'Heure', 'AttrTotalScore' => 'Score total', - 'AttrWeekday' => 'Semaine', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Temporisation arrêt', 'Available' => 'Disponibles', // Added - 2009-03-31 @@ -159,9 +171,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Le pourcentage de fusion de l\'image de référence doit être un entier supérieur à 0 et inférieur à 100', 'BadSectionLength' => 'La longueur de la section doit être un entier supérieur ou égal à 30', 'BadSignalCheckColour' => 'La chaîne de caractères pour la couleur d\'état du signal est invalide', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Le tampon d\'images pour la relecture doit être un entier supérieur ou égal à 0', 'BadWarmupCount' => 'Le nombre d\'images tests doit être un entier supérieur ou égal à 0', 'BadWebColour' => 'La chaîne de caractères pour la couleur web est invalide', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'La valeur de la largeur est invalide', 'Bandwidth' => 'Débit', 'BandwidthHead' => 'Débit', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -197,6 +211,7 @@ $SLANG = array( 'CanMoveRel' => 'Relatif', 'CanPan' => 'Panoramique' , 'CanReset' => 'RàZ', + 'CanReboot' => 'Can Reboot', 'CanSetPresets' => 'Stockage prépos.', 'CanSleep' => 'Veille', 'CanTilt' => 'Inclinaison', @@ -225,10 +240,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choisir une sélection de journaux', // Added - 2011-06-17 'ChoosePreset' => 'Choisir préréglage', 'Clear' => 'Effacer', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Fermer', 'Colour' => 'Couleur', 'Command' => 'Commande', 'Component' => 'Composant', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Config', 'ConfiguredFor' => 'Configuré pour', 'ConfirmDeleteEvents' => 'Etes-vous sûr de vouloir effacer le(s) événement(s) sélectionné(s)?', @@ -278,7 +295,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Réaliser détection native', 'Donate' => 'Veuillez faire un don', 'DonateAlready' => 'Non, j\'ai déjà donné', - 'DonateEnticement' => 'Vous utilisez ZoneMinder depuis quelque temps et nous espérons que vous trouvez cette solution utile. Bien que ZoneMinder est, et restera, une solution libre et ouverte (open source), son développement et son maintien nécessitent des moyens financiers. Si vous voulez aider au développement et à l\'ajout de fonctionnalités, veuillez considérer la possibilité d\'effectuer un don. Les dons sont bien sûr optionnels mais grandement appréciés et vous pouvez donner le montant que vous désirez.

Si vous voulez effectuer un don, veuillez sélectionner l\'option ci-dessous ou veuillez vous rendre sur http://www.zoneminder.com/donate.html à l\'aide de votre navigateur internet.

Merci d\'utiliser ZoneMinder et n\'oubliez pas de visiter les forums sur ZoneMinder.com pour le support ou des suggestions pour rendre votre expérience de ZoneMinder encore meilleure.', + 'DonateEnticement' => 'Vous utilisez ZoneMinder depuis quelque temps et nous espérons que vous trouvez cette solution utile. Bien que ZoneMinder est, et restera, une solution libre et ouverte (open source), son développement et son maintien nécessitent des moyens financiers. Si vous voulez aider au développement et à l\'ajout de fonctionnalités, veuillez considérer la possibilité d\'effectuer un don. Les dons sont bien sûr optionnels mais grandement appréciés et vous pouvez donner le montant que vous désirez.

Si vous voulez effectuer un don, veuillez sélectionner l\'option ci-dessous ou veuillez vous rendre sur https://zoneminder.com/donate/ à l\'aide de votre navigateur internet.

Merci d\'utiliser ZoneMinder et n\'oubliez pas de visiter les forums sur ZoneMinder.com pour le support ou des suggestions pour rendre votre expérience de ZoneMinder encore meilleure.', 'DonateRemindDay' => 'Pas encore, me rappeler dans 1 jour', 'DonateRemindHour' => 'Pas encore, me rappeler dans 1 heure', 'DonateRemindMonth' => 'Pas encore, me rappeler dans 1 mois', @@ -286,9 +303,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Pas encore, me rappeler dans 1 semaine', 'DonateYes' => 'Oui, je souhaiterais faire un don maintenant', 'Download' => 'Télécharger', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Dupliquer le nom de la caméra', // Added - 2009-03-31 'Duration' => 'Durée', 'Edit' => 'Editer', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Activer les alarmes', 'Enabled' => 'Activé', @@ -305,6 +324,7 @@ $SLANG = array( 'Events' => 'Evénements', 'Exclude' => 'Exclure', 'Execute' => 'Exécuter', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Exporter', 'ExportDetails' => 'Exporter détails événements', 'ExportFailed' => 'Exportation échouée', @@ -334,8 +354,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Exécuter une commande', 'FilterLog' => 'Filtre', // Added - 2015-04-18 'FilterMessageEvents' => 'Envoyer les détails par message', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filtre Px', 'FilterUnset' => 'Vous devez spécifier une largeur et une hauteur de filtre', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Transférer', 'FilterVideoEvents' => 'Créer vidéo', 'Filters' => 'Filtres', @@ -360,6 +382,7 @@ $SLANG = array( 'Function' => 'Mode', 'Gain' => 'Gain', 'General' => 'Général', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Générer vidéo', 'GeneratingVideo' => 'Génération vidéo', 'GoToZoneMinder' => 'Aller sur ZoneMinder.com', @@ -380,6 +403,7 @@ $SLANG = array( 'High' => 'Haut', 'HighBW' => 'Haut débit', 'Home' => 'Maison', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Heure', 'Hue' => 'Teinte', 'Id' => 'N°', @@ -404,6 +428,7 @@ $SLANG = array( 'Line' => 'Ligne', // Added - 2011-06-16 'LinkedMonitors' => 'Caméra(s) liée(s)', 'List' => 'Liste', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Charge', 'Local' => 'Local', 'Log' => 'Journal', // Added - 2011-06-16 @@ -490,6 +515,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'La liste ci-dessous montre les caméras détectées localement ou sur le réseau, qu\'elles soient déjà configurées ou non.

Sélectionnez la caméra désirée dans la liste.

Veuillez noter que toutes les caméras ne sont pas forcément détectées et que la sauvegarde entraînera l\'écrasement des paramètres déjà configurés pour la caméra en cours.

', // Added - 2009-03-31 'Monitors' => 'Caméras', 'Montage' => 'Montage', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Mois', 'More' => 'Plus', // Added - 2011-06-16 'MotionFrameSkip' => 'Saut image en alarme', @@ -516,6 +542,7 @@ $SLANG = array( 'Next' => 'Suivant', 'No' => 'Non', 'NoDetectedCameras' => 'Pas de caméras détectées', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Pas d\'images enregistrées pour cet événement', 'NoGroup' => 'Pas de groupe', 'NoSavedFilters' => 'Pas de filtres sauvegardés', @@ -534,6 +561,8 @@ $SLANG = array( 'OpGt' => 'sup. à', 'OpGtEq' => 'plus grand ou égal à', 'OpIn' => 'en lot', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'inf. à', 'OpLtEq' => 'inf. ou égal à', 'OpMatches' => 'correspond', @@ -543,6 +572,7 @@ $SLANG = array( 'Open' => 'Ouvrir', 'OptionHelp' => 'Aide', 'OptionRestartWarning' => 'Ces changements peuvent nécessiter un redémarrage de ZoneMinder pour être pleinement opérationnels.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Options', 'OrEnterNewName' => 'ou entrez nouv. nom', 'Order' => 'Ordre', @@ -580,9 +610,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'La liste ci-dessous montre les profils de flux existants pour la caméra sélectionnée.

Sélectionnez le profil désiré dans la liste ci-dessous.

Veuillez noter que ZoneMinder ne peut pas configurer de profils additionels et que la sauvegarde entraînera l\'écrasement des paramètres déjà configurés pour la caméra en cours.

', // Added - 2015-04-18 'Progress' => 'Progression', // Added - 2015-04-18 'Protocol' => 'Protocole', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Vitesse', 'Real' => 'Réel', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Enregistrer', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => '% fusion image référence', 'Refresh' => 'Rafraîchir', 'Remote' => 'Distant', @@ -598,6 +632,7 @@ $SLANG = array( 'ReplayAll' => 'Tous les événements', 'ReplayGapless' => 'Rejouer sans blancs', 'ReplaySingle' => 'Rejouer seul', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'RàZ', 'ResetEventCounts' => 'RàZ compteur évts', 'Restart' => 'Redémarrer', @@ -616,6 +651,7 @@ $SLANG = array( 'Save' => 'Sauvegarder', 'SaveAs' => 'Sauvegarder sous', 'SaveFilter' => 'Sauvegarder filtre', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Echelle', 'Score' => 'Score', 'Secs' => 'Secs', @@ -632,6 +668,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Filtres', 'ShowTimeline' => 'Afficher chronologie', 'SignalCheckColour' => 'Couleur vérif. signal', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Taille', 'SkinDescription' => 'Remplacer le skin par défaut', // Added - 2011-01-30 'Sleep' => 'Veille', @@ -651,6 +688,10 @@ $SLANG = array( 'State' => 'Etat', 'Stats' => 'Stats', 'Status' => 'Statut', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Pas', 'StepBack' => 'Reculer', 'StepForward' => 'Avancer', @@ -661,6 +702,8 @@ $SLANG = array( 'Stills' => 'Photos', 'Stop' => 'Arrêter', 'Stopped' => 'Arrêté', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Flux', 'StreamReplayBuffer' => 'Nb d\'image(s) pour relecture', 'Submit' => 'Soumettre', @@ -680,9 +723,9 @@ $SLANG = array( 'TimelineTip4' => 'Utilisez les contrôles ci-dessous pour faire un zoom arrière ou naviguer en arrière et avancer sur l\'intervalle de temps.', // Added 2013.08.15. 'Timestamp' => 'Horodatage', 'TimestampLabelFormat' => 'Format', + 'TimestampLabelSize' => 'Taille de police', 'TimestampLabelX' => 'Coordonnée X', 'TimestampLabelY' => 'Coordonnée Y', - 'TimestampLabelSize' => 'Taille de police', 'Today' => 'Aujourd\'hui', 'Tools' => 'Outils', 'Total' => 'Total', // Added - 2011-06-16 @@ -727,6 +770,7 @@ $SLANG = array( 'VideoGenParms' => 'Paramètres génération vidéo', 'VideoGenSucceeded' => 'Vidéo générée avec succès !', 'VideoSize' => 'Taille vidéo', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Voir', 'ViewAll' => 'Tout voir', 'ViewEvent' => 'Voir événement', @@ -736,6 +780,7 @@ $SLANG = array( 'Watch' => 'Regarder', 'Web' => 'Web', 'WebColour' => 'Couleur web', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Semaine', 'White' => 'Blanc', 'WhiteBalance' => 'Balance des blancs', diff --git a/web/lang/he_il.php b/web/lang/he_il.php index f8c8897a4..ff4440053 100644 --- a/web/lang/he_il.php +++ b/web/lang/he_il.php @@ -50,7 +50,7 @@ // do this by default, uncomment this if required. // // Example -header( "Content-Type: text/html; charset=iso-8859-8-i" ); +header( "Content-Type: text/html; charset=UTF-8" ); // You may need to change your locale here if your default one is incorrect for the // language described in this file, or if you have multiple languages supported. @@ -71,62 +71,74 @@ setlocale( LC_ALL, 'he_IL' ); //All locale settings 4.3.0 and after // Simple String Replacements $SLANG = array( - '24BitColour' => 'öáò 24 áéè', - '32BitColour' => 'öáò 32 áéè', // Added - 2011-06-15 - '8BitGrey' => 'âååðé àôåø 8 áéè', - 'Action' => 'ôòåìä', - 'Actual' => 'î÷åøé', - 'AddNewControl' => 'äåñó ÷åðèøåì çãù', - 'AddNewMonitor' => 'äåñó îåðéèåø çãù', - 'AddNewUser' => 'äåñó îùúîù çãù', - 'AddNewZone' => 'äåñó àéæåø çãù', - 'Alarm' => 'àæò÷ä', - 'AlarmBrFrames' => 'àæò÷ú
ôøééîéí', - 'AlarmFrame' => 'àæò÷ú ôøééîéí', - 'AlarmFrameCount' => 'ñôéøú àæò÷åú ôøééîéí', - 'AlarmLimits' => 'äâáìåú àæò÷ä', + '24BitColour' => 'צבע 24 ביט', + '32BitColour' => 'צבע 32 ביט', // Added - 2011-06-15 + '8BitGrey' => 'גווני אפור 8 ביט', + 'Action' => 'פעולה', + 'Actual' => 'מקורי', + 'AddNewControl' => 'הוסף קונטרול חדש', + 'AddNewMonitor' => 'הוסף מוניטור חדש', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 + 'AddNewUser' => 'הוסף משתמש חדש', + 'AddNewZone' => 'הוסף איזור חדש', + 'Alarm' => 'אזעקה', + 'AlarmBrFrames' => 'אזעקת
פריימים', + 'AlarmFrame' => 'אזעקת פריימים', + 'AlarmFrameCount' => 'ספירת אזעקות פריימים', + 'AlarmLimits' => 'הגבלות אזעקה', 'AlarmMaximumFPS' => 'Alarm Maximum FPS', - 'AlarmPx' => 'àæò÷ú Px', - 'AlarmRGBUnset' => 'äéðê çééá ìàúçì àæò÷ú öáò', + 'AlarmPx' => 'אזעקת Px', + 'AlarmRGBUnset' => 'הינך חייב לאתחל אזעקת צבע', 'AlarmRefImageBlendPct'=> 'Alarm Reference Image Blend %ge', // Added - 2015-04-18 - 'Alert' => 'äúøàä', - 'All' => 'äëì', + 'Alert' => 'התראה', + 'All' => 'הכל', 'AnalysisFPS' => 'Analysis FPS', // Added - 2015-07-22 'AnalysisUpdateDelay' => 'Analysis Update Delay', // Added - 2015-07-23 - 'Apply' => 'äçì', - 'ApplyingStateChange' => 'äçì ùéðåé îöá', - 'ArchArchived' => 'àøëéá áìáã', - 'ArchUnarchived' => 'ìà ìàøëéá áìáã', - 'Archive' => 'àøëéá', - 'Archived' => 'àåøëá', - 'Area' => 'àæåø', - 'AreaUnits' => 'àæåø (px/%)', + 'Apply' => 'החל', + 'ApplyingStateChange' => 'החל שינוי מצב', + 'ArchArchived' => 'ארכיב בלבד', + 'ArchUnarchived' => 'לא לארכיב בלבד', + 'Archive' => 'ארכיב', + 'Archived' => 'אורכב', + 'Area' => 'אזור', + 'AreaUnits' => 'אזור (px/%)', 'AttrAlarmFrames' => 'Alarm Frames', 'AttrArchiveStatus' => 'Archive Status', - 'AttrAvgScore' => 'ðé÷åã îîåöò', - 'AttrCause' => 'ñéáä', - 'AttrDate' => 'úàøéê', - 'AttrDateTime' => 'úàøéê/ùòä', + 'AttrAvgScore' => 'ניקוד ממוצע', + 'AttrCause' => 'סיבה', 'AttrDiskBlocks' => 'Disk Blocks', 'AttrDiskPercent' => 'Disk Percent', - 'AttrDuration' => 'îùê æîï', - 'AttrFrames' => 'ôøééîéí', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 + 'AttrDuration' => 'משך זמן', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 + 'AttrFrames' => 'פריימים', 'AttrId' => 'Id', - 'AttrMaxScore' => 'ðé÷åã î÷ñéîìé', + 'AttrMaxScore' => 'ניקוד מקסימלי', 'AttrMonitorId' => 'Monitor Id', - 'AttrMonitorName' => 'ùí îåðéèåø', - 'AttrName' => 'ùí', - 'AttrNotes' => 'äòøåú', + 'AttrMonitorName' => 'שם מוניטור', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 + 'AttrName' => 'שם', + 'AttrNotes' => 'הערות', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'System Load', - 'AttrTime' => 'ùòä', - 'AttrTotalScore' => 'ñê ñëåí', - 'AttrWeekday' => 'éåí áùáåò', - 'Auto' => 'àåèå', - 'AutoStopTimeout' => 'ôñ÷ æîï òöéøä àåèå', + 'AttrTotalScore' => 'סך סכום', + 'Auto' => 'אוטו', + 'AutoStopTimeout' => 'פסק זמן עצירה אוטו', 'Available' => 'Available', // Added - 2009-03-31 - 'AvgBrScore' => 'ðé÷åã
îîåöò', - 'Background' => 'ø÷ò', - 'BackgroundFilter' => 'äøõ îñðï áø÷ò', + 'AvgBrScore' => 'ניקוד
ממוצע', + 'Background' => 'רקע', + 'BackgroundFilter' => 'הרץ מסנן ברקע', 'BadAlarmFrameCount' => 'Alarm frame count must be an integer of one or more', 'BadAlarmMaxFPS' => 'Alarm Maximum FPS must be a positive integer or floating point value', 'BadAnalysisFPS' => 'Analysis FPS must be a positive integer or floating point value', // Added - 2015-07-22 @@ -153,28 +165,30 @@ $SLANG = array( 'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer', 'BadSectionLength' => 'Section length must be an integer of 30 or more', 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', + 'BadSourceType' => 'Source Type "Web Site" requires the Function to be set to "Monitor"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Stream replay buffer must be an integer of zero or more', 'BadWarmupCount' => 'Warmup frames must be an integer of zero or more', 'BadWebColour' => 'Web colour must be a valid web colour string', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Width must be set to a valid value', - 'Bandwidth' => 'øåçá ôñ', + 'Bandwidth' => 'רוחב פס', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing 'BlobPx' => 'Blob Px', 'BlobSizes' => 'Blob Sizes', 'Blobs' => 'Blobs', - 'Brightness' => 'áäéøåú', + 'Brightness' => 'בהירות', 'Buffer' => 'Buffer', // Added - 2015-04-18 'Buffers' => 'Buffers', 'CSSDescription' => 'Change the default css for this computer', // Added - 2015-04-18 - 'CanAutoFocus' => 'àôùø äúî÷ãåú àåèåîèé', + 'CanAutoFocus' => 'אפשר התמקדות אוטומטי', 'CanAutoGain' => 'Can Auto Gain', 'CanAutoIris' => 'Can Auto Iris', 'CanAutoWhite' => 'Can Auto White Bal.', - 'CanAutoZoom' => 'àôùø æåí àåèåîèé', - 'CanFocus' => 'àôùø äúî÷ãåú', - 'CanFocusAbs' => 'àôùø äúî÷ãåú àáñåìåèé', - 'CanFocusCon' => 'àôùø äúî÷ãåú îúîùê', - 'CanFocusRel' => 'àôùø äúî÷ãåú éçñé', + 'CanAutoZoom' => 'אפשר זום אוטומטי', + 'CanFocus' => 'אפשר התמקדות', + 'CanFocusAbs' => 'אפשר התמקדות אבסולוטי', + 'CanFocusCon' => 'אפשר התמקדות מתמשך', + 'CanFocusRel' => 'אפשר התמקדות יחסי', 'CanGain' => 'Can Gain ', 'CanGainAbs' => 'Can Gain Absolute', 'CanGainCon' => 'Can Gain Continuous', @@ -183,136 +197,141 @@ $SLANG = array( 'CanIrisAbs' => 'Can Iris Absolute', 'CanIrisCon' => 'Can Iris Continuous', 'CanIrisRel' => 'Can Iris Relative', - 'CanMove' => 'àôùø úðåòä', - 'CanMoveAbs' => 'àôùø úðåòä àáñåìåèéú', - 'CanMoveCon' => 'àôùø úæåæä îúîùëú', + 'CanMove' => 'אפשר תנועה', + 'CanMoveAbs' => 'אפשר תנועה אבסולוטית', + 'CanMoveCon' => 'אפשר תזוזה מתמשכת', 'CanMoveDiag' => 'Can Move Diagonally', 'CanMoveMap' => 'Can Move Mapped', - 'CanMoveRel' => 'àôùø úæåæä éçñéú', + 'CanMoveRel' => 'אפשר תזוזה יחסית', 'CanPan' => 'Can Pan' , - 'CanReset' => 'àôùø àúçåì', + 'CanReset' => 'אפשר אתחול', 'CanSetPresets' => 'Can Set Presets', - 'CanSleep' => 'àôùø îöá ùéðä', - 'CanTilt' => 'àôùø æòæåò', - 'CanWake' => 'àôùø éöéàä îîöá ùéðä', + 'CanSleep' => 'אפשר מצב שינה', + 'CanTilt' => 'אפשר זעזוע', + 'CanWake' => 'אפשר יציאה ממצב שינה', 'CanWhite' => 'Can White Balance', 'CanWhiteAbs' => 'Can White Bal. Absolute', 'CanWhiteBal' => 'Can White Bal.', 'CanWhiteCon' => 'Can White Bal. Continuous', 'CanWhiteRel' => 'Can White Bal. Relative', - 'CanZoom' => 'àôùø æåí', - 'CanZoomAbs' => 'àôùø æåí àáñåìåèé', - 'CanZoomCon' => 'àôùø æåí îúîùê', - 'CanZoomRel' => 'àôùø æåí éçñé', - 'Cancel' => 'áèì', + 'CanZoom' => 'אפשר זום', + 'CanZoomAbs' => 'אפשר זום אבסולוטי', + 'CanZoomCon' => 'אפשר זום מתמשך', + 'CanZoomRel' => 'אפשר זום יחסי', + 'Cancel' => 'בטל', 'CancelForcedAlarm' => 'Cancel Forced Alarm', 'CaptureHeight' => 'Capture Height', 'CaptureMethod' => 'Capture Method', // Added - 2009-02-08 'CapturePalette' => 'Capture Palette', 'CaptureResolution' => 'Capture Resolution', // Added - 2015-04-18 'CaptureWidth' => 'Capture Width', - 'Cause' => 'ñéáä', + 'Cause' => 'סיבה', 'CheckMethod' => 'Alarm Check Method', 'ChooseDetectedCamera' => 'Choose Detected Camera', // Added - 2009-03-31 - 'ChooseFilter' => 'áçø îñðï', + 'ChooseFilter' => 'בחר מסנן', 'ChooseLogFormat' => 'Choose a log format', // Added - 2011-06-17 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Choose Preset', 'Clear' => 'Clear', // Added - 2011-06-16 - 'Close' => 'ñâåø', - 'Colour' => 'öáò', - 'Command' => 'ô÷åãä', + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 + 'Close' => 'סגור', + 'Colour' => 'צבע', + 'Command' => 'פקודה', 'Component' => 'Component', // Added - 2011-06-16 - 'Config' => 'úöåøä', - 'ConfiguredFor' => 'úöåøä òáåø', + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 + 'Config' => 'תצורה', + 'ConfiguredFor' => 'תצורה עבור', 'ConfirmDeleteEvents' => 'Are you sure you wish to delete the selected events?', - 'ConfirmPassword' => 'àùø ñéñîà', - 'ConjAnd' => 'å', - 'ConjOr' => 'àå', - 'Console' => '÷åðñåì', - 'ContactAdmin' => 'öåø ÷ùø òí îðäì äîòøëú áùáéì ôøèéí ðåñôéí.', - 'Continue' => 'äîùê', - 'Contrast' => 'ðéâåãéåú', - 'Control' => '÷åðèøåì', - 'ControlAddress' => 'ëúåáú ä÷åðèøåì', - 'ControlCap' => 'éëåìú ä÷åðèøåì', - 'ControlCaps' => 'éëåìåú ä÷åðèøåì', - 'ControlDevice' => 'äú÷ï ä÷åðèøåì', - 'ControlType' => 'ñåâ ä÷åðèøåì', + 'ConfirmPassword' => 'אשר סיסמא', + 'ConjAnd' => 'ו', + 'ConjOr' => 'או', + 'Console' => 'קונסול', + 'ContactAdmin' => 'צור קשר עם מנהל המערכת בשביל פרטים נוספים.', + 'Continue' => 'המשך', + 'Contrast' => 'ניגודיות', + 'Control' => 'קונטרול', + 'ControlAddress' => 'כתובת הקונטרול', + 'ControlCap' => 'יכולת הקונטרול', + 'ControlCaps' => 'יכולות הקונטרול', + 'ControlDevice' => 'התקן הקונטרול', + 'ControlType' => 'סוג הקונטרול', 'Controllable' => 'Controllable', 'Current' => 'Current', // Added - 2015-04-18 - 'Cycle' => 'îçæåøé', - 'CycleWatch' => 'öôééä îçæåøéú', + 'Cycle' => 'מחזורי', + 'CycleWatch' => 'צפייה מחזורית', 'DateTime' => 'Date/Time', // Added - 2011-06-16 - 'Day' => 'éåí', + 'Day' => 'יום', 'Debug' => 'Debug', 'DefaultRate' => 'Default Rate', 'DefaultScale' => 'Default Scale', 'DefaultView' => 'Default View', 'Deinterlacing' => 'Deinterlacing', // Added - 2015-04-18 'Delay' => 'Delay', // Added - 2015-04-18 - 'Delete' => 'îç÷', - 'DeleteAndNext' => 'îç÷ & äáà', - 'DeleteAndPrev' => 'îç÷ & ä÷åãí', - 'DeleteSavedFilter' => 'îç÷ îñðï ùîåø', - 'Description' => 'úéàåø', + 'Delete' => 'מחק', + 'DeleteAndNext' => 'מחק & הבא', + 'DeleteAndPrev' => 'מחק & הקודם', + 'DeleteSavedFilter' => 'מחק מסנן שמור', + 'Description' => 'תיאור', 'DetectedCameras' => 'Detected Cameras', // Added - 2009-03-31 'DetectedProfiles' => 'Detected Profiles', // Added - 2015-04-18 'Device' => 'Device', // Added - 2009-02-08 - 'DeviceChannel' => 'òøåõ ääú÷ï', - 'DeviceFormat' => 'úáðéú ääú÷ï', - 'DeviceNumber' => 'îñôø ääú÷ï', - 'DevicePath' => 'ðúéá ääú÷ï', - 'Devices' => 'äú÷ðéí', - 'Dimensions' => 'îéîãéí', - 'DisableAlarms' => 'ðèøì àæò÷åú', - 'Disk' => 'ãéñ÷', + 'DeviceChannel' => 'ערוץ ההתקן', + 'DeviceFormat' => 'תבנית ההתקן', + 'DeviceNumber' => 'מספר ההתקן', + 'DevicePath' => 'נתיב ההתקן', + 'Devices' => 'התקנים', + 'Dimensions' => 'מימדים', + 'DisableAlarms' => 'נטרל אזעקות', + 'Disk' => 'דיסק', 'Display' => 'Display', // Added - 2011-01-30 'Displaying' => 'Displaying', // Added - 2011-06-16 'DoNativeMotionDetection'=> 'Do Native Motion Detection', - 'Donate' => 'úøåí áá÷ùä', - 'DonateAlready' => 'ìà, úøîúé ëáø', + 'Donate' => 'תרום בבקשה', + 'DonateAlready' => 'לא, תרמתי כבר', 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', - 'DonateRemindDay' => 'òãééï ìà, äæëø ìà áòåã éåí àçã', - 'DonateRemindHour' => 'òãééï ìà, äæëø ìé áòåã ùòä àçú', - 'DonateRemindMonth' => 'òãééï ìà, äæëø ìé áòåã çåãù àçã', - 'DonateRemindNever' => 'ìà, àðé ìà øåöä ìúøåí, àì úúæëø àåúé', - 'DonateRemindWeek' => 'òãééï ìà, äæëø ìé áòåã ùáåò àçã', - 'DonateYes' => 'ëï, àðé îòåðééï ìúøåí òëùéå', - 'Download' => 'äåøã', + 'DonateRemindDay' => 'עדיין לא, הזכר לא בעוד יום אחד', + 'DonateRemindHour' => 'עדיין לא, הזכר לי בעוד שעה אחת', + 'DonateRemindMonth' => 'עדיין לא, הזכר לי בעוד חודש אחד', + 'DonateRemindNever' => 'לא, אני לא רוצה לתרום, אל תתזכר אותי', + 'DonateRemindWeek' => 'עדיין לא, הזכר לי בעוד שבוע אחד', + 'DonateYes' => 'כן, אני מעוניין לתרום עכשיו', + 'Download' => 'הורד', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 - 'Duration' => 'îùê æîï', - 'Edit' => 'òøåê', - 'Email' => 'ãåà"ì', - 'EnableAlarms' => 'àôùø àæò÷åú', - 'Enabled' => 'àôùø', - 'EnterNewFilterName' => 'äæï îñðï çãù', - 'Error' => 'ùâéàä', + 'Duration' => 'משך זמן', + 'Edit' => 'ערוך', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 + 'Email' => 'דוא"ל', + 'EnableAlarms' => 'אפשר אזעקות', + 'Enabled' => 'אפשר', + 'EnterNewFilterName' => 'הזן מסנן חדש', + 'Error' => 'שגיאה', 'ErrorBrackets' => 'Error, please check you have an equal number of opening and closing brackets', 'ErrorValidValue' => 'Error, please check that all terms have a valid value', - 'Etc' => 'åëå\'', - 'Event' => 'àéøåò', - 'EventFilter' => 'îñðï àéøåò', - 'EventId' => 'æéäåé àéøåò', - 'EventName' => 'ùí àéøåò', + 'Etc' => 'וכו\'', + 'Event' => 'אירוע', + 'EventFilter' => 'מסנן אירוע', + 'EventId' => 'זיהוי אירוע', + 'EventName' => 'שם אירוע', 'EventPrefix' => 'Event Prefix', - 'Events' => 'àéøåòéí', - 'Exclude' => 'ììà', - 'Execute' => 'áöò', - 'Export' => 'éöà', - 'ExportDetails' => 'éöà ôøèé àéøåò', - 'ExportFailed' => 'éöåà ðëùì', - 'ExportFormat' => 'éöà úáðéú ÷åáõ', + 'Events' => 'אירועים', + 'Exclude' => 'ללא', + 'Execute' => 'בצע', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 + 'Export' => 'יצא', + 'ExportDetails' => 'יצא פרטי אירוע', + 'ExportFailed' => 'יצוא נכשל', + 'ExportFormat' => 'יצא תבנית קובץ', 'ExportFormatTar' => 'Tar', 'ExportFormatZip' => 'Zip', 'ExportFrames' => 'Export Frame Details', - 'ExportImageFiles' => 'éöà ÷áöé úîåðä', + 'ExportImageFiles' => 'יצא קבצי תמונה', 'ExportLog' => 'Export Log', // Added - 2011-06-17 - 'ExportMiscFiles' => 'éöà ÷áöéí àçøéí (àí éùðí)', - 'ExportOptions' => 'éöà àôùøåéåú', + 'ExportMiscFiles' => 'יצא קבצים אחרים (אם ישנם)', + 'ExportOptions' => 'יצא אפשרויות', 'ExportSucceeded' => 'Export Succeeded', // Added - 2009-02-08 'ExportVideoFiles' => 'Export Video Files (if present)', - 'Exporting' => 'îééöà', + 'Exporting' => 'מייצא', 'FPS' => 'fps', 'FPSReportInterval' => 'FPS Report Interval', 'FTP' => 'FTP', @@ -320,20 +339,22 @@ $SLANG = array( 'FastForward' => 'Fast Forward', 'Feed' => 'Feed', 'Ffmpeg' => 'Ffmpeg', // Added - 2009-02-08 - 'File' => '÷åáõ', + 'File' => 'קובץ', 'Filter' => 'Filter', // Added - 2015-04-18 - 'FilterArchiveEvents' => 'àøëá úåàîéí', - 'FilterDeleteEvents' => 'îç÷ úåàîéí', - 'FilterEmailEvents' => 'ùìç ãåàø ùì ëì äúåàîéí', + 'FilterArchiveEvents' => 'ארכב תואמים', + 'FilterDeleteEvents' => 'מחק תואמים', + 'FilterEmailEvents' => 'שלח דואר של כל התואמים', 'FilterExecuteEvents' => 'Execute command on all matches', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Message details of all matches', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filter Px', - 'FilterUnset' => 'òìéê ìöééï øåçá åâåáä îñðï', - 'FilterUploadEvents' => 'òìä àú ëì äúåàîéí', - 'FilterVideoEvents' => 'öåø åéãàå ìëì äúåàîéí', - 'Filters' => 'îñððéí', - 'First' => 'äøàùåï', + 'FilterUnset' => 'עליך לציין רוחב וגובה מסנן', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 + 'FilterUploadEvents' => 'עלה את כל התואמים', + 'FilterVideoEvents' => 'צור וידאו לכל התואמים', + 'Filters' => 'מסננים', + 'First' => 'הראשון', 'FlippedHori' => 'Flipped Horizontally', 'FlippedVert' => 'Flipped Vertically', 'FnMocord' => 'Mocord', // Added 2013.08.16. @@ -342,24 +363,25 @@ $SLANG = array( 'FnNodect' => 'Nodect', // Added 2013.08.16. 'FnNone' => 'None', // Added 2013.08.16. 'FnRecord' => 'Record', // Added 2013.08.16. - 'Focus' => 'äúî÷ã', - 'ForceAlarm' => 'äëøç àæò÷ä', - 'Format' => 'úáðéú', - 'Frame' => 'ôøééí', + 'Focus' => 'התמקד', + 'ForceAlarm' => 'הכרח אזעקה', + 'Format' => 'תבנית', + 'Frame' => 'פריים', 'FrameId' => 'Frame Id', 'FrameRate' => 'Frame Rate', - 'FrameSkip' => 'ãìâ ôøééí', - 'Frames' => 'ôøééîéí', - 'Func' => 'ôåð÷', - 'Function' => 'ôåð÷öéä', + 'FrameSkip' => 'דלג פריים', + 'Frames' => 'פריימים', + 'Func' => 'פונק', + 'Function' => 'פונקציה', 'Gain' => 'Gain', - 'General' => 'ëììé', - 'GenerateVideo' => 'öåø åéãàå', - 'GeneratingVideo' => 'îééöø åéãàå', - 'GoToZoneMinder' => 'á÷ø ZoneMinder.com', - 'Grey' => 'àôåø', - 'Group' => '÷áåöä', - 'Groups' => '÷áåöåú', + 'General' => 'כללי', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 + 'GenerateVideo' => 'צור וידאו', + 'GeneratingVideo' => 'מייצר וידאו', + 'GoToZoneMinder' => 'בקר ZoneMinder.com', + 'Grey' => 'אפור', + 'Group' => 'קבוצה', + 'Groups' => 'קבוצות', 'HasFocusSpeed' => 'Has Focus Speed', 'HasGainSpeed' => 'Has Gain Speed', 'HasHomePreset' => 'Has Home Preset', @@ -371,51 +393,53 @@ $SLANG = array( 'HasTurboTilt' => 'Has Turbo Tilt', 'HasWhiteSpeed' => 'Has White Bal. Speed', 'HasZoomSpeed' => 'Has Zoom Speed', - 'High' => 'âáåä', - 'HighBW' => 'âáåä ø/ô', - 'Home' => 'áéú', - 'Hour' => 'ùòä', + 'High' => 'גבוה', + 'HighBW' => 'גבוה ר/פ', + 'Home' => 'בית', + 'Hostname' => 'Hostname', // Added - 2018-08-30 + 'Hour' => 'שעה', 'Hue' => 'Hue', - 'Id' => 'æéäåé', - 'Idle' => 'äîúðä', - 'Ignore' => 'äúòìí', - 'Image' => 'úîåðä', + 'Id' => 'זיהוי', + 'Idle' => 'המתנה', + 'Ignore' => 'התעלם', + 'Image' => 'תמונה', 'ImageBufferSize' => 'Image Buffer Size (frames)', - 'Images' => 'úîåðåú', - 'In' => 'áúåê', - 'Include' => 'ëìåì', - 'Inverted' => 'äôåê', + 'Images' => 'תמונות', + 'In' => 'בתוך', + 'Include' => 'כלול', + 'Inverted' => 'הפוך', 'Iris' => 'Iris', - 'KeyString' => 'îçøåæú úåéí', - 'Label' => 'úååéú', - 'Language' => 'ùôä', - 'Last' => 'àçøåï', + 'KeyString' => 'מחרוזת תוים', + 'Label' => 'תווית', + 'Language' => 'שפה', + 'Last' => 'אחרון', 'Layout' => 'Layout', // Added - 2009-02-08 'Level' => 'Level', // Added - 2011-06-16 'Libvlc' => 'Libvlc', - 'LimitResultsPost' => 'úåöàåú áìáã;', // This is used at the end of the phrase 'Limit to first N results only' - 'LimitResultsPre' => 'äâáì ìøàùåï', // This is used at the beginning of the phrase 'Limit to first N results only' + 'LimitResultsPost' => 'תוצאות בלבד;', // This is used at the end of the phrase 'Limit to first N results only' + 'LimitResultsPre' => 'הגבל לראשון', // This is used at the beginning of the phrase 'Limit to first N results only' 'Line' => 'Line', // Added - 2011-06-16 - 'LinkedMonitors' => 'îåðéèåøéí î÷åùøéí', - 'List' => 'øùéîä', - 'Load' => 'èòï', - 'Local' => 'î÷åîé', + 'LinkedMonitors' => 'מוניטורים מקושרים', + 'List' => 'רשימה', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 + 'Load' => 'טען', + 'Local' => 'מקומי', 'Log' => 'Log', // Added - 2011-06-16 - 'LoggedInAs' => 'äúçáø ë', + 'LoggedInAs' => 'התחבר כ', 'Logging' => 'Logging', // Added - 2011-06-16 - 'LoggingIn' => 'îúçáø', - 'Login' => 'äúçáø', - 'Logout' => 'äúðú÷', + 'LoggingIn' => 'מתחבר', + 'Login' => 'התחבר', + 'Logout' => 'התנתק', 'Logs' => 'Logs', // Added - 2011-06-17 - 'Low' => 'ðîåê', - 'LowBW' => 'ðîåê ø/ô', - 'Main' => 'îøëæé', - 'Man' => 'îãøéê', - 'Manual' => 'îãøéê', - 'Mark' => 'ñîï', - 'Max' => 'î÷ñ', - 'MaxBandwidth' => 'øåçá ôñ î÷ñ', - 'MaxBrScore' => 'ðé÷åã
î÷ñéîìé', + 'Low' => 'נמוך', + 'LowBW' => 'נמוך ר/פ', + 'Main' => 'מרכזי', + 'Man' => 'מדריך', + 'Manual' => 'מדריך', + 'Mark' => 'סמן', + 'Max' => 'מקס', + 'MaxBandwidth' => 'רוחב פס מקס', + 'MaxBrScore' => 'ניקוד
מקסימלי', 'MaxFocusRange' => 'Max Focus Range', 'MaxFocusSpeed' => 'Max Focus Speed', 'MaxFocusStep' => 'Max Focus Step', @@ -438,8 +462,8 @@ $SLANG = array( 'MaxZoomSpeed' => 'Max Zoom Speed', 'MaxZoomStep' => 'Max Zoom Step', 'MaximumFPS' => 'Maximum FPS', - 'Medium' => 'áéðåðé', - 'MediumBW' => 'Medium B/W', + 'Medium' => 'בינוני', + 'MediumBW' => 'Medium B/W', 'Message' => 'Message', // Added - 2011-06-16 'MinAlarmAreaLtMax' => 'Minimum alarm area should be less than maximum', 'MinAlarmAreaUnset' => 'You must specify the minimum alarm pixel count', @@ -476,18 +500,19 @@ $SLANG = array( 'MinZoomStep' => 'Min Zoom Step', 'Misc' => 'Misc', 'Mode' => 'Mode', // Added - 2015-04-18 - 'Monitor' => 'îåðéèåø', - 'MonitorIds' => 'Monitor Ids', + 'Monitor' => 'מוניטור', + 'MonitorIds' => 'Monitor Ids', 'MonitorPreset' => 'Monitor Preset', 'MonitorPresetIntro' => 'Select an appropriate preset from the list below.

Please note that this may overwrite any values you already have configured for this monitor.

', 'MonitorProbe' => 'Monitor Probe', // Added - 2009-03-31 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 - 'Monitors' => 'îåðéèåøéí', + 'Monitors' => 'מוניטורים', 'Montage' => 'Montage', - 'Month' => 'çåãù', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 + 'Month' => 'חודש', 'More' => 'More', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', - 'Move' => 'äææ', + 'Move' => 'הזז', 'Mtg2widgrd' => '2-wide grid', // Added 2013.08.15. 'Mtg3widgrd' => '3-wide grid', // Added 2013.08.15. 'Mtg3widgrx' => '3-wide grid, scaled, enlarge on alarm', // Added 2013.08.15. @@ -498,48 +523,52 @@ $SLANG = array( 'MustConfirmPassword' => 'You must confirm the password', 'MustSupplyPassword' => 'You must supply a password', 'MustSupplyUsername' => 'You must supply a username', - 'Name' => 'ùí', - 'Near' => 'ìéã', - 'Network' => 'øùú', - 'New' => 'çãù', - 'NewGroup' => '÷áåöä çãùä', - 'NewLabel' => 'úååéú çãùä', - 'NewPassword' => 'ñéñîà çãùä', - 'NewState' => 'îöá çãù', - 'NewUser' => 'îùúîù çãù', - 'Next' => 'äáà', - 'No' => 'ìà', + 'Name' => 'שם', + 'Near' => 'ליד', + 'Network' => 'רשת', + 'New' => 'חדש', + 'NewGroup' => 'קבוצה חדשה', + 'NewLabel' => 'תווית חדשה', + 'NewPassword' => 'סיסמא חדשה', + 'NewState' => 'מצב חדש', + 'NewUser' => 'משתמש חדש', + 'Next' => 'הבא', + 'No' => 'לא', 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'There are no frames recorded for this event', - 'NoGroup' => 'ììà ÷áåöä', + 'NoGroup' => 'ללא קבוצה', 'NoSavedFilters' => 'NoSavedFilters', 'NoStatisticsRecorded' => 'There are no statistics recorded for this event/frame', - 'None' => 'øé÷', - 'NoneAvailable' => 'áìúé æîéï', - 'Normal' => 'ðåøîìé', + 'None' => 'ריק', + 'NoneAvailable' => 'בלתי זמין', + 'Normal' => 'נורמלי', 'Notes' => 'Notes', 'NumPresets' => 'Num Presets', - 'Off' => 'ëáåé', - 'On' => 'ãìå÷', + 'Off' => 'כבוי', + 'On' => 'דלוק', 'OnvifCredentialsIntro'=> 'Please supply user name and password for the selected camera.
If no user has been created for the camera then the user given here will be created with the given password.

', // Added - 2015-04-18 'OnvifProbe' => 'ONVIF', // Added - 2015-04-18 'OnvifProbeIntro' => 'The list below shows detected ONVIF cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 - 'OpEq' => 'ùååä ì', - 'OpGt' => 'âãåì î', + 'OpEq' => 'שווה ל', + 'OpGt' => 'גדול מ', 'OpGtEq' => 'greater than or equal to', 'OpIn' => 'in set', - 'OpLt' => 'ôçåú î', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 + 'OpLt' => 'פחות מ', 'OpLtEq' => 'less than or equal to', 'OpMatches' => 'matches', - 'OpNe' => 'àéðå ùååä', + 'OpNe' => 'אינו שווה', 'OpNotIn' => 'not in set', - 'OpNotMatches' => 'àéðå úåàí', - 'Open' => 'ôúç', + 'OpNotMatches' => 'אינו תואם', + 'Open' => 'פתח', 'OptionHelp' => 'OptionHelp', 'OptionRestartWarning' => 'These changes may not come into effect fully\nwhile the system is running. When you have\nfinished making your changes please ensure that\nyou restart ZoneMinder.', - 'Options' => 'àôùøåéåú', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 + 'Options' => 'אפשרויות', 'OrEnterNewName' => 'or enter new name', - 'Order' => 'îéåï', + 'Order' => 'מיון', 'Orientation' => 'Orientation', 'Out' => 'Out', 'OverwriteExisting' => 'Overwrite Existing', @@ -548,200 +577,216 @@ $SLANG = array( 'PanLeft' => 'Pan Left', 'PanRight' => 'Pan Right', 'PanTilt' => 'Pan/Tilt', - 'Parameter' => 'ôøîèø', - 'Password' => 'ñéñîà', + 'Parameter' => 'פרמטר', + 'Password' => 'סיסמא', 'PasswordsDifferent' => 'The new and confirm passwords are different', - 'Paths' => 'ðúéáéí', + 'Paths' => 'נתיבים', 'Pause' => 'Pause', - 'Phone' => 'èìôåï', - 'PhoneBW' => 'ø/ô èìôåï', + 'Phone' => 'טלפון', + 'PhoneBW' => 'ר/פ טלפון', 'Pid' => 'PID', // Added - 2011-06-16 'PixelDiff' => 'Pixel Diff', - 'Pixels' => 'ôé÷ñìéí', + 'Pixels' => 'פיקסלים', 'Play' => 'Play', - 'PlayAll' => 'ðâï äëì', - 'PleaseWait' => 'äîúï áá÷ùä', + 'PlayAll' => 'נגן הכל', + 'PleaseWait' => 'המתן בבקשה', 'Plugins' => 'Plugins', - 'Point' => 'ð÷åãä', + 'Point' => 'נקודה', 'PostEventImageBuffer' => 'Post Event Image Count', 'PreEventImageBuffer' => 'Pre Event Image Count', 'PreserveAspect' => 'Preserve Aspect Ratio', 'Preset' => 'Preset', 'Presets' => 'Presets', - 'Prev' => 'ä÷åãí', + 'Prev' => 'הקודם', 'Probe' => 'Probe', // Added - 2009-03-31 'ProfileProbe' => 'Stream Probe', // Added - 2015-04-18 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocol', - 'Rate' => 'ãéøåâ', - 'Real' => 'àîéúé', - 'Record' => 'ä÷ìèä', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 + 'Rate' => 'דירוג', + 'Real' => 'אמיתי', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 + 'Record' => 'הקלטה', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Reference Image Blend %ge', - 'Refresh' => 'øòðåï', - 'Remote' => 'îøåç÷', - 'RemoteHostName' => 'ùí îàøç îøåç÷', - 'RemoteHostPath' => 'ðúéá îàøç îøåç÷', - 'RemoteHostPort' => 'ôåøè îàøç îøåç÷', + 'Refresh' => 'רענון', + 'Remote' => 'מרוחק', + 'RemoteHostName' => 'שם מארח מרוחק', + 'RemoteHostPath' => 'נתיב מארח מרוחק', + 'RemoteHostPort' => 'פורט מארח מרוחק', 'RemoteHostSubPath' => 'Remote Host SubPath', // Added - 2009-02-08 'RemoteImageColours' => 'Remote Image Colours', 'RemoteMethod' => 'Remote Method', // Added - 2009-02-08 'RemoteProtocol' => 'Remote Protocol', // Added - 2009-02-08 - 'Rename' => 'ùðä ùí', + 'Rename' => 'שנה שם', 'Replay' => 'Replay', 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', - 'Reset' => 'àôñ', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 + 'Reset' => 'אפס', 'ResetEventCounts' => 'Reset Event Counts', - 'Restart' => 'àúçì', - 'Restarting' => 'îàúçì', + 'Restart' => 'אתחל', + 'Restarting' => 'מאתחל', 'RestrictedCameraIds' => 'Restricted Camera Ids', 'RestrictedMonitors' => 'Restricted Monitors', - 'ReturnDelay' => 'çæøä îäùäéä', - 'ReturnLocation' => 'îé÷åí çæøä', + 'ReturnDelay' => 'חזרה מהשהיה', + 'ReturnLocation' => 'מיקום חזרה', 'Rewind' => 'Rewind', - 'RotateLeft' => 'ñåáá ùîàìä', - 'RotateRight' => 'ñåáá éîéðä', + 'RotateLeft' => 'סובב שמאלה', + 'RotateRight' => 'סובב ימינה', 'RunLocalUpdate' => 'Please run zmupdate.pl to update', // Added - 2011-05-25 - 'RunMode' => 'öåøú øéöä', - 'RunState' => 'îöá øéöä', - 'Running' => 'îøéõ', - 'Save' => 'ùîåø', - 'SaveAs' => 'ùîåø áùí', - 'SaveFilter' => 'ùîåø îñðï', - 'Scale' => 'ñ÷àìä', - 'Score' => 'ðé÷åã', - 'Secs' => 'ùðéåú', - 'Sectionlength' => 'àåøê ÷èò', - 'Select' => 'áçø', + 'RunMode' => 'צורת ריצה', + 'RunState' => 'מצב ריצה', + 'Running' => 'מריץ', + 'Save' => 'שמור', + 'SaveAs' => 'שמור בשם', + 'SaveFilter' => 'שמור מסנן', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 + 'Scale' => 'סקאלה', + 'Score' => 'ניקוד', + 'Secs' => 'שניות', + 'Sectionlength' => 'אורך קטע', + 'Select' => 'בחר', 'SelectFormat' => 'Select Format', // Added - 2011-06-17 'SelectLog' => 'Select Log', // Added - 2011-06-17 - 'SelectMonitors' => 'áçø îåðéèåøéí', + 'SelectMonitors' => 'בחר מוניטורים', 'SelfIntersecting' => 'Polygon edges must not intersect', - 'Set' => '÷áò', + 'Set' => 'קבע', 'SetNewBandwidth' => 'Set New Bandwidth', 'SetPreset' => 'Set Preset', - 'Settings' => 'äâãøåú', + 'Settings' => 'הגדרות', 'ShowFilterWindow' => 'Show Filter Window', 'ShowTimeline' => 'Show Timeline', 'SignalCheckColour' => 'Signal Check Colour', - 'Size' => 'âåãì', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 + 'Size' => 'גודל', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 - 'Sleep' => 'ùéðä', + 'Sleep' => 'שינה', 'SortAsc' => 'Asc', 'SortBy' => 'Sort by', 'SortDesc' => 'Desc', - 'Source' => 'î÷åø', + 'Source' => 'מקור', 'SourceColours' => 'Source Colours', // Added - 2009-02-08 'SourcePath' => 'Source Path', // Added - 2009-02-08 - 'SourceType' => 'ñåâ î÷åø', - 'Speed' => 'îäéøåú', - 'SpeedHigh' => 'îäéøåú âáåää', - 'SpeedLow' => 'îäéøåú ðîåëä', - 'SpeedMedium' => 'îöìîä áéðåðéú', - 'SpeedTurbo' => 'îäéøåú èåøáå', - 'Start' => 'äúçì', - 'State' => 'îöá', - 'Stats' => 'îöáéí', - 'Status' => 'ñèèåñ', - 'Step' => 'öòã', + 'SourceType' => 'סוג מקור', + 'Speed' => 'מהירות', + 'SpeedHigh' => 'מהירות גבוהה', + 'SpeedLow' => 'מהירות נמוכה', + 'SpeedMedium' => 'מצלמה בינונית', + 'SpeedTurbo' => 'מהירות טורבו', + 'Start' => 'התחל', + 'State' => 'מצב', + 'Stats' => 'מצבים', + 'Status' => 'סטטוס', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 + 'Step' => 'צעד', 'StepBack' => 'Step Back', 'StepForward' => 'Step Forward', - 'StepLarge' => 'öòã âãåì', - 'StepMedium' => 'öòã áéðåðé', - 'StepNone' => 'àì úöòã', - 'StepSmall' => 'öòã ÷èï', - 'Stills' => 'ñèéìñ', - 'Stop' => 'òöåø', - 'Stopped' => 'ðòöø', - 'Stream' => 'ñèøéí', + 'StepLarge' => 'צעד גדול', + 'StepMedium' => 'צעד בינוני', + 'StepNone' => 'אל תצעד', + 'StepSmall' => 'צעד קטן', + 'Stills' => 'סטילס', + 'Stop' => 'עצור', + 'Stopped' => 'נעצר', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 + 'Stream' => 'סטרים', 'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Submit' => 'Submit', - 'System' => 'îòøëú', + 'System' => 'מערכת', 'SystemLog' => 'System Log', // Added - 2011-06-16 'TargetColorspace' => 'Target colorspace', // Added - 2015-04-18 - 'Tele' => 'èì', + 'Tele' => 'טל', 'Thumbnail' => 'Thumbnail', 'Tilt' => 'Tilt', - 'Time' => 'æîï', - 'TimeDelta' => 'ùéðåé áæîï', - 'TimeStamp' => 'çåúîú æîï', - 'Timeline' => '÷å æîï', + 'Time' => 'זמן', + 'TimeDelta' => 'שינוי בזמן', + 'TimeStamp' => 'חותמת זמן', + 'Timeline' => 'קו זמן', 'TimelineTip1' => 'Pass your mouse over the graph to view a snapshot image and event details.', // Added 2013.08.15. 'TimelineTip2' => 'Click on the coloured sections of the graph, or the image, to view the event.', // Added 2013.08.15. 'TimelineTip3' => 'Click on the background to zoom in to a smaller time period based around your click.', // Added 2013.08.15. 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. - 'Timestamp' => 'çåúîú æîï', + 'Timestamp' => 'חותמת זמן', 'TimestampLabelFormat' => 'Timestamp Label Format', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'Timestamp Label X', 'TimestampLabelY' => 'Timestamp Label Y', - 'Today' => 'äéåí', - 'Tools' => 'ëìéí', + 'Today' => 'היום', + 'Tools' => 'כלים', 'Total' => 'Total', // Added - 2011-06-16 - 'TotalBrScore' => 'ñê
ðé÷åã', + 'TotalBrScore' => 'סך
ניקוד', 'TrackDelay' => 'Track Delay', 'TrackMotion' => 'Track Motion', - 'Triggers' => 'èøéâøéí', + 'Triggers' => 'טריגרים', 'TurboPanSpeed' => 'Turbo Pan Speed', 'TurboTiltSpeed' => 'Turbo Tilt Speed', - 'Type' => 'ñåâ', - 'Unarchive' => 'áìúé àøëéá', + 'Type' => 'סוג', + 'Unarchive' => 'בלתי ארכיב', 'Undefined' => 'Undefined', // Added - 2009-02-08 - 'Units' => 'éçéãåú', - 'Unknown' => 'áìúé éãåò', - 'Update' => 'òãëåï', - 'UpdateAvailable' => 'òãëåï ìæåï-îéðãø àôùøé.', - 'UpdateNotNecessary' => 'òãëåï àéðå äëøçé.', + 'Units' => 'יחידות', + 'Unknown' => 'בלתי ידוע', + 'Update' => 'עדכון', + 'UpdateAvailable' => 'עדכון לזון-מינדר אפשרי.', + 'UpdateNotNecessary' => 'עדכון אינו הכרחי.', 'Updated' => 'Updated', // Added - 2011-06-16 'Upload' => 'Upload', // Added - 2011-08-23 - 'UseFilter' => 'ùéîåù áîñðï', - 'UseFilterExprsPost' => ' filter expressions', // This is used at the end of the phrase 'use N filter expressions' - 'UseFilterExprsPre' => 'ùéîåù ', // This is used at the beginning of the phrase 'use N filter expressions' + 'UseFilter' => 'שימוש במסנן', + 'UseFilterExprsPost' => ' filter expressions', // This is used at the end of the phrase 'use N filter expressions' + 'UseFilterExprsPre' => 'שימוש ', // This is used at the beginning of the phrase 'use N filter expressions' 'UsedPlugins' => 'Used Plugins', - 'User' => 'îùúîù', - 'Username' => 'ùí îùúîù', - 'Users' => 'îùúîùéí', + 'User' => 'משתמש', + 'Username' => 'שם משתמש', + 'Users' => 'משתמשים', 'V4L' => 'V4L', // Added - 2015-04-18 'V4LCapturesPerFrame' => 'Captures Per Frame', // Added - 2015-04-18 'V4LMultiBuffer' => 'Multi Buffering', // Added - 2015-04-18 - 'Value' => 'òøê', - 'Version' => 'âéøñä', - 'VersionIgnore' => 'äúòìí îâéøñä æå', - 'VersionRemindDay' => 'äæëø ìé áòåã éåí àçã', - 'VersionRemindHour' => 'äæëø ìé áòåã ùòä àçú', + 'Value' => 'ערך', + 'Version' => 'גירסה', + 'VersionIgnore' => 'התעלם מגירסה זו', + 'VersionRemindDay' => 'הזכר לי בעוד יום אחד', + 'VersionRemindHour' => 'הזכר לי בעוד שעה אחת', 'VersionRemindNever' => 'Don\'t remind about new versions', 'VersionRemindWeek' => 'Remind again in 1 week', - 'Video' => 'åéãàå', - 'VideoFormat' => 'úáðéú åéãàå', + 'Video' => 'וידאו', + 'VideoFormat' => 'תבנית וידאו', 'VideoGenFailed' => 'Video Generation Failed!', 'VideoGenFiles' => 'Existing Video Files', 'VideoGenNoFiles' => 'No Video Files Found', 'VideoGenParms' => 'Video Generation Parameters', 'VideoGenSucceeded' => 'Video Generation Succeeded!', - 'VideoSize' => 'âåãì åéãàå', - 'View' => 'äöâ', - 'ViewAll' => 'äöâ äëì', - 'ViewEvent' => 'äöâ àéøåò', + 'VideoSize' => 'גודל וידאו', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 + 'View' => 'הצג', + 'ViewAll' => 'הצג הכל', + 'ViewEvent' => 'הצג אירוע', 'ViewPaged' => 'View Paged', - 'Wake' => 'äòø', + 'Wake' => 'הער', 'WarmupFrames' => 'Warmup Frames', - 'Watch' => 'öôä', - 'Web' => 'àéðèøðè', - 'WebColour' => 'öáò àéðèøðè', - 'Week' => 'ùáåò', - 'White' => 'ìáï', + 'Watch' => 'צפה', + 'Web' => 'אינטרנט', + 'WebColour' => 'צבע אינטרנט', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 + 'Week' => 'שבוע', + 'White' => 'לבן', 'WhiteBalance' => 'White Balance', - 'Wide' => 'øçá', + 'Wide' => 'רחב', 'X' => 'X', 'X10' => 'X10', 'X10ActivationString' => 'X10 Activation String', 'X10InputAlarmString' => 'X10 Input Alarm String', 'X10OutputAlarmString' => 'X10 Output Alarm String', 'Y' => 'Y', - 'Yes' => 'ëï', - 'YouNoPerms' => 'àéï ìê äøùàä ìäéëðñ ìî÷åø æä.', - 'Zone' => 'àæåø', + 'Yes' => 'כן', + 'YouNoPerms' => 'אין לך הרשאה להיכנס למקור זה.', + 'Zone' => 'אזור', 'ZoneAlarmColour' => 'Alarm Colour (Red/Green/Blue)', 'ZoneArea' => 'Zone Area', 'ZoneExtendAlarmFrames' => 'Extend Alarm Frame Count', @@ -753,10 +798,10 @@ $SLANG = array( 'ZoneMinMaxPixelThres' => 'Min/Max Pixel Threshold (0-255)', 'ZoneMinderLog' => 'ZoneMinder Log', // Added - 2011-06-17 'ZoneOverloadFrames' => 'Overload Frame Ignore Count', - 'Zones' => 'àæåøéí', - 'Zoom' => 'æåí', - 'ZoomIn' => 'æåí ôðéîä', - 'ZoomOut' => 'æåí äçåöä', + 'Zones' => 'אזורים', + 'Zoom' => 'זום', + 'ZoomIn' => 'זום פנימה', + 'ZoomOut' => 'זום החוצה', ); // Complex replacements with formatting and/or placements, must be passed through sprintf diff --git a/web/lang/hu_hu.php b/web/lang/hu_hu.php index 3f0928be1..7ef51dab3 100644 --- a/web/lang/hu_hu.php +++ b/web/lang/hu_hu.php @@ -121,6 +121,8 @@ $SLANG = array( 'Actual' => 'Valós', 'AddNewControl' => 'Új vezérlés', 'AddNewMonitor' => 'Új kamera', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Új felhasználó', 'AddNewZone' => 'Új zóna', 'Alarm' => 'Riadó', @@ -148,22 +150,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Archivált állapot', 'AttrAvgScore' => 'Átlagérték', 'AttrCause' => 'Okozó', - 'AttrDate' => 'Dátum', - 'AttrDateTime' => 'Dátum/Idő', 'AttrDiskBlocks' => 'Tárhely blokk', 'AttrDiskPercent' => 'Tárhely százalék', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Időtartam', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Képkocka', 'AttrId' => 'Azonosító', 'AttrMaxScore' => 'Max. érték', 'AttrMonitorId' => 'Kamera azonosító', 'AttrMonitorName' => 'Kamera név', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Név', 'AttrNotes' => 'Megjegyzés', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'Rendszerterhelés', - 'AttrTime' => 'Idő', 'AttrTotalScore' => 'Összérték', - 'AttrWeekday' => 'Hétköznap', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Auto megállási idő túllépés', 'Available' => 'Elérhető', @@ -196,9 +208,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Változás a referenciaképtől legyen legalább 1', 'BadSectionLength' => 'Fix időtartamú esemény hossza legyen legalább 30', 'BadSignalCheckColour' => 'Szín a jel kimaradásakor legyen egy érvényes HTML szín-kód', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Folyam visszajátszó puffer legyen legalább 0', 'BadWarmupCount' => 'Bemelegítő képkockák száma legyen legalább 0', 'BadWebColour' => 'Szín az idővonal ablakban legyen egy érvényes HTML szín-kód', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'A képszélesség legyen érvényes érték képpontban megadva', 'Bandwidth' => 'Sávszélesség', 'BandwidthHead' => 'sávszélességre', @@ -234,6 +248,7 @@ $SLANG = array( 'CanMoveRel' => 'Relatíven tud mozogni', 'CanPan' => 'Tud jobb-bal mozgást' , 'CanReset' => 'Tud alaphelyzetbe jönni', + 'CanReboot' => 'Can Reboot', 'CanSetPresets' => 'Tud menteni profilokat', 'CanSleep' => 'Tud phihenő üzemmódot', 'CanTilt' => 'Tud fel-le mozgást', @@ -262,10 +277,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Válasszon naplót', 'ChoosePreset' => 'Válasszon profilt', 'Clear' => 'Törlés', + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Bezárás', 'Colour' => 'Szín', 'Command' => 'Parancs', 'Component' => 'Komponens', + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Beállítás', 'ConfiguredFor' => 'Beállítva', 'ConfirmDeleteEvents' => 'Biztos benne, hogy törli a kiválasztott eseményeket?', @@ -315,7 +332,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18 'Donate' => 'Kérem támogasson', 'DonateAlready' => 'Nem, én már támogattam', - 'DonateEnticement' => 'Ön már jó ideje használja a ZoneMindert, és reméljük hasznos eszköznek tartja háza vagy munkahelye biztonságában. Bár a ZoneMinder egy szabad, nyílt forráskódú termék és az is marad, a fejlesztése pénzbe kerül. Ha van lehetősége támogatni a jövőbeni fejlesztéseket és az új funkciókat kérem, tegye meg. A támogatás teljesen önkéntes, de nagyon megbecsült és mértéke is tetszőleges.

Ha támogatni szertne, kérem, válasszon az alábbi lehetőségekből vagy látogassa meg a http://www.zoneminder.com/donate.html oldalt.

Köszönjük, hogy használja a ZoneMinder-t és ne felejtse el meglátogatni a fórumokat a ZoneMinder.com oldalon támogatásért és ötletekért, hogy a jövőben is még jobban ki tudja használni a ZoneMinder lehetőségeit.', + 'DonateEnticement' => 'Ön már jó ideje használja a ZoneMindert, és reméljük hasznos eszköznek tartja háza vagy munkahelye biztonságában. Bár a ZoneMinder egy szabad, nyílt forráskódú termék és az is marad, a fejlesztése pénzbe kerül. Ha van lehetősége támogatni a jövőbeni fejlesztéseket és az új funkciókat kérem, tegye meg. A támogatás teljesen önkéntes, de nagyon megbecsült és mértéke is tetszőleges.

Ha támogatni szertne, kérem, válasszon az alábbi lehetőségekből vagy látogassa meg a https://zoneminder.com/donate/ oldalt.

Köszönjük, hogy használja a ZoneMinder-t és ne felejtse el meglátogatni a fórumokat a ZoneMinder.com oldalon támogatásért és ötletekért, hogy a jövőben is még jobban ki tudja használni a ZoneMinder lehetőségeit.', 'DonateRemindDay' => 'Nem most, figyelmeztessen egy nap múlva', 'DonateRemindHour' => 'Nem most, figyelmeztessen egy óra múlva', 'DonateRemindMonth' => 'Nem most, figyelmeztessen egy hónap múlva', @@ -323,9 +340,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Nem most, figyelmeztessen egy hét múlva', 'DonateYes' => 'Igen, most szeretném támogatni', 'Download' => 'Letöltés', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Kameranév duplikálás', 'Duration' => 'Időtartam', 'Edit' => 'Szerkesztés', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'E-mail', 'EnableAlarms' => 'Riasztások engedélyezése', 'Enabled' => 'Engedélyezve', @@ -342,6 +361,7 @@ $SLANG = array( 'Events' => 'Események', 'Exclude' => 'Kizárás', 'Execute' => 'Végrehajtás', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Exportálás', 'ExportDetails' => 'Esemény adatainak exportálása', 'ExportFailed' => 'Az exportálás sikertelen', @@ -371,8 +391,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Parancs futtatása minden találaton', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Minden találat adatainak üzenése', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Szűrt képkockák', 'FilterUnset' => 'Meg kell adnod a szűrő szélességét és magasságát', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Minden találat feltöltése', 'FilterVideoEvents' => 'Videó készítése minden találatról', 'Filters' => 'Szűrők', @@ -397,6 +419,7 @@ $SLANG = array( 'Function' => 'Funkció', 'Gain' => 'Erősítés', 'General' => 'Általános', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Videó készítés', 'GeneratingVideo' => 'Videó készítése folyamatban', 'GoToZoneMinder' => 'Ellenőrzés a ZoneMinder.com-on', @@ -417,6 +440,7 @@ $SLANG = array( 'High' => 'Magas', 'HighBW' => 'Magas
sávszél', 'Home' => 'Alapba', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Órában', 'Hue' => 'Színárnyalat', 'Id' => 'Az.', @@ -441,6 +465,7 @@ $SLANG = array( 'Line' => 'Sor', 'LinkedMonitors' => 'Összefüggés más kamerákkal
(jelölés Ctrl+kattintással)', 'List' => 'Lista', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Terhelés', 'Local' => 'Helyi', 'Log' => 'Napló', @@ -527,6 +552,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'Az alábbi listában találhatók az automatikusan érzékelt analóg és hálózati kamerákat, illetve azt, hogy közülük melyik van használatban, vagy kiválasztható.

Válasszon egyet az alábbi listából.

Figyelem: nem biztos, hogy minden kamerát lehet automatikusan érzékelni. Az itt kiválasztott kamara adatai felülírhatják azokat, amelyeket már ehhez a monitorhoz beállított.

', 'Monitors' => 'Kamerák', 'Montage' => 'Többkamerás nézet', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Hónapban', 'More' => 'Több', 'MotionFrameSkip' => 'Motion Frame Skip', @@ -553,6 +579,7 @@ $SLANG = array( 'Next' => 'Következő', 'No' => 'Nem', 'NoDetectedCameras' => 'Nem érzékelhetőek kamerák', + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Nincs rögzített képkocka ehhez az eseményhez', 'NoGroup' => 'Nincs csoport', 'NoSavedFilters' => 'Nincs mentett szűrő', @@ -571,6 +598,8 @@ $SLANG = array( 'OpGt' => 'nagyobb mint', 'OpGtEq' => 'nagyobb van egyenlő', 'OpIn' => 'beállítva', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'kisebb mint', 'OpLtEq' => 'kisebb vagy egyenlő', 'OpMatches' => 'találatok', @@ -580,6 +609,7 @@ $SLANG = array( 'Open' => 'Megnyitás', 'OptionHelp' => 'Beállítási segítség', 'OptionRestartWarning' => 'Ez a beállítás nem tud érvénybe lépni miközben az élő rendszer fut.\nHa végzett minden beállítással, kérem, indítsa újra a ZoneMinder szolgáltatást.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Beállítások', 'OrEnterNewName' => 'vagy új néven:', 'Order' => 'Sorrend', @@ -617,9 +647,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'FPS', 'Real' => 'Valós', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Felvétel', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Változás a referenciaképtől %-ban', 'Refresh' => 'Frissítés', 'Remote' => 'Hálózati', @@ -635,6 +669,7 @@ $SLANG = array( 'ReplayAll' => 'Mindet', 'ReplayGapless' => 'Szünet nélkülieket', 'ReplaySingle' => 'Egyenként', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Alapértékre', 'ResetEventCounts' => 'Eseményszámláló nullázása', 'Restart' => 'A szolgáltatás újraindítása', @@ -653,6 +688,7 @@ $SLANG = array( 'Save' => 'Mentés', 'SaveAs' => 'Mentés erre:', 'SaveFilter' => 'Szűrő mentése', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Méret', 'Score' => 'Pontszám', 'Secs' => 'mp.', @@ -669,6 +705,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Szűrőablak megjelenítése', 'ShowTimeline' => 'Idővonal megjelenítése', 'SignalCheckColour' => 'Szín a jel kimaradásakor', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Fájlméret', 'SkinDescription' => 'Alapértelmezett felület ebben a böngészőben', 'Sleep' => 'Alvás', @@ -688,6 +725,10 @@ $SLANG = array( 'State' => 'Állapot', 'Stats' => 'Statisztikák', 'Status' => 'Státusz', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Ugrás', 'StepBack' => 'Visszalépés', 'StepForward' => 'Előrelépés', @@ -698,6 +739,8 @@ $SLANG = array( 'Stills' => 'Állóképek', 'Stop' => 'A szolgáltatás leállítása', 'Stopped' => 'Leállítva', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Élő folyam', 'StreamReplayBuffer' => 'Képkockák száma a pufferben visszajátszáskor', 'Submit' => 'Küldés', @@ -717,6 +760,7 @@ $SLANG = array( 'TimelineTip4' => 'Használja az alábbi gombokat hogy az időskálát csúsztassa, vagy kicsinyítse.', 'Timestamp' => 'Időbélyeg', 'TimestampLabelFormat' => 'Időbélyeg formátuma', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'Elhelyezés X pozició', 'TimestampLabelY' => 'Elhelyezés Y pozició', 'Today' => 'Ma', @@ -763,6 +807,7 @@ $SLANG = array( 'VideoGenParms' => 'Videó készítési paraméterek', 'VideoGenSucceeded' => 'A videó elkészült.', 'VideoSize' => 'Képméret', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Megtekintés', 'ViewAll' => 'Az összes listázása', 'ViewEvent' => 'Események nézet', @@ -772,6 +817,7 @@ $SLANG = array( 'Watch' => 'Figyelés', 'Web' => 'Web', 'WebColour' => 'Szín az idővonal skálán', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Héten', 'White' => 'Fehér', 'WhiteBalance' => 'Fehér egyensúly', diff --git a/web/lang/it_it.php b/web/lang/it_it.php index 338f2c07f..cf2dbab8d 100644 --- a/web/lang/it_it.php +++ b/web/lang/it_it.php @@ -83,6 +83,8 @@ $SLANG = array( 'Actual' => 'Attuale', 'AddNewControl' => 'Aggiungi nuovo Controllo', 'AddNewMonitor' => 'Aggiungi nuovo Monitor', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Aggiungi nuovo Utente', 'AddNewZone' => 'Aggiungi nuova Zona', 'Alarm' => 'Allarme', @@ -110,22 +112,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Stato Archivio', 'AttrAvgScore' => 'Punteggio medio', 'AttrCause' => 'Causa', - 'AttrDate' => 'Data', - 'AttrDateTime' => 'Data/Ora', 'AttrDiskBlocks' => 'Blocchi del Disco', 'AttrDiskPercent' => 'Percentuale del Disco', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Durata', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Immagini', 'AttrId' => 'Id', 'AttrMaxScore' => 'Punteggio massimo', 'AttrMonitorId' => 'Id Monitor', 'AttrMonitorName' => 'Nome Monitor', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Nome', 'AttrNotes' => 'Note', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'System Load', - 'AttrTime' => 'Ora', 'AttrTotalScore' => 'Punteggio totale', - 'AttrWeekday' => 'Giorno della settimana', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Auto Stop Timeout', 'Available' => 'Disponibile', // Added - 2009-03-31 @@ -158,9 +170,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'La percentuale di miscela di riferimento deve essere un intero positivo', 'BadSectionLength' => 'La lunghezza della sezione deve essere un numero intero pari a 30 o maggiore', 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Stream replay buffer must be an integer of zero or more', 'BadWarmupCount' => 'Il numero di frame di allarme deve essere un numero intero maggiore o uguale a zero', 'BadWebColour' => 'L\'identificativo del colore deve essere una stringa valida', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'La larghezza deve essere impostata con un valore valido', 'Bandwidth' => 'Banda', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -196,6 +210,7 @@ $SLANG = array( 'CanMoveRel' => 'Puo\' Mov. Relativo', 'CanPan' => 'Puo\' Pan' , 'CanReset' => 'Puo\' Reset', + 'CanReboot' => 'Can Reboot', 'CanSetPresets' => 'Puo\' impostare preset', 'CanSleep' => 'Puo\' andare in sleep', 'CanTilt' => 'Puo\' Tilt', @@ -224,10 +239,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Scegli Preset', 'Clear' => 'Clear', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Chiudi', 'Colour' => 'Colori', 'Command' => 'Comando', 'Component' => 'Component', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Configura', 'ConfiguredFor' => 'Configurato per', 'ConfirmDeleteEvents' => 'Sei sicuro di voler cancellare gli eventi selezionati', @@ -277,7 +294,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'Donate,per favore', 'DonateAlready' => 'No, ho gia donato... ', - 'DonateEnticement' => 'Stai usando ZoneMinder da un po\' di tempo e spero che tu lo stia trovando utile per la sicurezza di casa tua o del tuo posto di lavoro..Anche se ZoneMinder e\' distribuito liberamente come software libero,costa soldi sia svilupparlo che supportarlo. Se preferisci che questo software continui ad avere supporto e sviluppo in futuro allora considera l\idea di fare una piccola donazione. Donare e\' ovviamente opzionale, ma apprezzato e puoi donare quanto vuoi,quel poco o tanto che tu desideri.

Se hai voglia per cortesia seleziona l\'opzione sotto o punta il tuo browser a http://www.zoneminder.com/donate.html .

Grazie per usare ZoneMinder e non dimenticare di visitare il forum in ZoneMinder.com se cerchi supporto o hai suggerimenti riguardo a come rendere migliore Zoneminder.', + 'DonateEnticement' => 'Stai usando ZoneMinder da un po\' di tempo e spero che tu lo stia trovando utile per la sicurezza di casa tua o del tuo posto di lavoro..Anche se ZoneMinder e\' distribuito liberamente come software libero,costa soldi sia svilupparlo che supportarlo. Se preferisci che questo software continui ad avere supporto e sviluppo in futuro allora considera l\idea di fare una piccola donazione. Donare e\' ovviamente opzionale, ma apprezzato e puoi donare quanto vuoi,quel poco o tanto che tu desideri.

Se hai voglia per cortesia seleziona l\'opzione sotto o punta il tuo browser a https://zoneminder.com/donate/ .

Grazie per usare ZoneMinder e non dimenticare di visitare il forum in ZoneMinder.com se cerchi supporto o hai suggerimenti riguardo a come rendere migliore Zoneminder.', 'DonateRemindDay' => 'Non ancora, ricordamelo ancora tra 1 giorno', 'DonateRemindHour' => 'Non ancora, ricordamelo ancora tra 1 ora', 'DonateRemindMonth' => 'Non ancora, ricordamelo ancora tra 1 mese', @@ -285,9 +302,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Non ancora, ricordamelo ancora tra 1 settimana', 'DonateYes' => 'Si,mi piacerebbe donare qualcosa ora', 'Download' => 'Download', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Il nome del monitor e\' gia\' presente', // Added - 2009-03-31 'Duration' => 'Durata', 'Edit' => 'Modifica', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Abilita Allarmi', 'Enabled' => 'Attivo', @@ -304,6 +323,7 @@ $SLANG = array( 'Events' => 'Eventi', 'Exclude' => 'Escludi', 'Execute' => 'Esegui', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Esporta', 'ExportDetails' => 'Esp. dettagli eventi', 'ExportFailed' => 'Esp. Fallita ', @@ -333,8 +353,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Esegui un comando', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Invia dettagli tramite messaggio', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Px Filtro', 'FilterUnset' => 'Devi specificare altezza e larghezza per il filtro', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Fai upload eventi (FTP)', 'FilterVideoEvents' => 'Crea video per tutte le corrispondenze', 'Filters' => 'Filtri', @@ -359,6 +381,7 @@ $SLANG = array( 'Function' => 'Funzione', 'Gain' => 'Gain', 'General' => 'Generale', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Genera Video', 'GeneratingVideo' => 'Sto generando il Video', 'GoToZoneMinder' => 'Vai su zoneminder.com', @@ -379,6 +402,7 @@ $SLANG = array( 'High' => 'Alta', 'HighBW' => 'Banda Alta', 'Home' => 'Home', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Ora', 'Hue' => 'Tinta', 'Id' => 'Id', @@ -403,6 +427,7 @@ $SLANG = array( 'Line' => 'Line', // Added - 2011-06-16 'LinkedMonitors' => 'Monitor Collegati', 'List' => 'Lista', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Carico Sistema', 'Local' => 'Locale', 'Log' => 'Log', // Added - 2011-06-16 @@ -489,6 +514,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 'Monitors' => 'Monitors', 'Montage' => 'Montaggio', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Mese', 'More' => 'More', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -515,6 +541,7 @@ $SLANG = array( 'Next' => 'Prossimo', 'No' => 'No', 'NoDetectedCameras' => 'Nessuna telecamera rilevata', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Non ci sono immagini salvate per questo evento', 'NoGroup' => 'Nessun gruppo', // Added - 2009-02-08 'NoSavedFilters' => 'NessunFiltroSalvato', @@ -533,6 +560,8 @@ $SLANG = array( 'OpGt' => 'maggiore di', 'OpGtEq' => 'maggiore o uguale a', 'OpIn' => 'impostato', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'minore di', 'OpLtEq' => 'minore o uguale a', 'OpMatches' => 'corrisponde', @@ -542,6 +571,7 @@ $SLANG = array( 'Open' => 'Apri', 'OptionHelp' => 'Opzioni di Aiuto', 'OptionRestartWarning' => 'Queste modifiche potrebbero essere attive solo dopo un riavvio del sistema. Riavviare ZoneMinder.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Opzioni', 'OrEnterNewName' => 'o inserisci un nuovo nome', 'Order' => 'Ordine', @@ -579,9 +609,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Velocita\'', 'Real' => 'Reale', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Registra', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Riferimento Miscela Immagine percentuale', 'Refresh' => 'Aggiorna', 'Remote' => 'Remoto', @@ -597,6 +631,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Resetta', 'ResetEventCounts' => 'Resetta Contatore Eventi', 'Restart' => 'Riavvia', @@ -615,6 +650,7 @@ $SLANG = array( 'Save' => 'Salva', 'SaveAs' => 'Salva come', 'SaveFilter' => 'salva Filtro', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Scala', 'Score' => 'Punteggio', 'Secs' => 'Secs', @@ -631,6 +667,7 @@ $SLANG = array( 'ShowFilterWindow' => 'MostraFinestraFiltri', 'ShowTimeline' => 'Mostra linea temporale', 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'grandezza', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 'Sleep' => 'Sleep', @@ -650,6 +687,10 @@ $SLANG = array( 'State' => 'Stato', 'Stats' => 'Statistiche', 'Status' => 'Stato', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Passo', 'StepBack' => 'Step Back', 'StepForward' => 'Step Forward', @@ -660,6 +701,8 @@ $SLANG = array( 'Stills' => 'Foto', 'Stop' => 'Stop', 'Stopped' => 'Inattivo', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Flusso', 'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Submit' => 'Accetta', @@ -679,6 +722,7 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => 'Timestamp', 'TimestampLabelFormat' => 'Formato etichetta timestamp', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'coordinata X etichetta', 'TimestampLabelY' => 'coordinata Y etichetta', 'Today' => 'Oggi ', @@ -725,6 +769,7 @@ $SLANG = array( 'VideoGenParms' => 'Parametri Generazione Video', 'VideoGenSucceeded' => 'Successo: Generato Video !', 'VideoSize' => 'Dimensioni Video', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'vedi', 'ViewAll' => 'Vedi Tutto', 'ViewEvent' => 'Vedi Evento', @@ -734,6 +779,7 @@ $SLANG = array( 'Watch' => 'Guarda', 'Web' => 'Web', 'WebColour' => 'Colore Web', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Settimana', 'White' => 'Bianco', 'WhiteBalance' => 'Bil. Bianco ', diff --git a/web/lang/ja_jp.php b/web/lang/ja_jp.php index 340d2caac..55b13ae0a 100644 --- a/web/lang/ja_jp.php +++ b/web/lang/ja_jp.php @@ -79,6 +79,8 @@ $SLANG = array( 'Actual' => '生中継', 'AddNewControl' => 'Add New Control', 'AddNewMonitor' => 'モニター追加', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'ユーザ追加', 'AddNewZone' => 'ゾーン追加', 'Alarm' => 'アラーム', @@ -106,22 +108,32 @@ $SLANG = array( 'AttrArchiveStatus' => '保存状態', 'AttrAvgScore' => '平均スコアー', 'AttrCause' => 'Cause', - 'AttrDate' => '日付', - 'AttrDateTime' => '日時', 'AttrDiskBlocks' => 'Disk Blocks', 'AttrDiskPercent' => 'Disk Percent', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => '継続時間', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'フレーム', 'AttrId' => 'Id', 'AttrMaxScore' => '最高スコアー', 'AttrMonitorId' => 'モニター Id', 'AttrMonitorName' => 'モニター 名前', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Name', 'AttrNotes' => 'Notes', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'System Load', - 'AttrTime' => '時間', 'AttrTotalScore' => '合計スコアー', - 'AttrWeekday' => '曜日', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Auto Stop Timeout', 'Available' => 'Available', // Added - 2009-03-31 @@ -154,9 +166,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer', 'BadSectionLength' => 'Section length must be an integer of 30 or more', 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Stream replay buffer must be an integer of zero or more', 'BadWarmupCount' => 'Warmup frames must be an integer of zero or more', 'BadWebColour' => 'Web colour must be a valid web colour string', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Width must be set to a valid value', 'Bandwidth' => '帯域幅', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -192,6 +206,7 @@ $SLANG = array( 'CanMoveRel' => 'Can Move Relative', 'CanPan' => 'Can Pan' , 'CanReset' => 'Can Reset', + 'CanReboot' => 'Can Reboot', 'CanSetPresets' => 'Can Set Presets', 'CanSleep' => 'Can Sleep', 'CanTilt' => 'Can Tilt', @@ -220,10 +235,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Choose Preset', 'Clear' => 'Clear', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => '閉じる', 'Colour' => '色', 'Command' => 'Command', 'Component' => 'Component', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Config', 'ConfiguredFor' => '設定:', 'ConfirmDeleteEvents' => 'Are you sure you wish to delete the selected events?', @@ -273,7 +290,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'Please Donate', 'DonateAlready' => 'No, I\'ve already donated', - 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', + 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', 'DonateRemindDay' => 'Not yet, remind again in 1 day', 'DonateRemindHour' => 'Not yet, remind again in 1 hour', 'DonateRemindMonth' => 'Not yet, remind again in 1 month', @@ -281,12 +298,14 @@ $SLANG = array( 'DonateRemindWeek' => 'Not yet, remind again in 1 week', 'DonateYes' => 'Yes, I\'d like to donate now', 'Download' => 'Download', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 'Duration' => '継続時間', 'Edit' => '編集', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'メール', 'EnableAlarms' => 'Enable Alarms', - 'Enabled' => '使用可能\', + 'Enabled' => '使用可能', 'EnterNewFilterName' => '新しいフィルター名の入力', 'Error' => 'エラー', 'ErrorBrackets' => 'エラー、開き括弧と閉じ括弧の数が合っているのかを確認してください', @@ -300,6 +319,7 @@ $SLANG = array( 'Events' => 'イベント', 'Exclude' => '排除', 'Execute' => 'Execute', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Export', 'ExportDetails' => 'Export Event Details', 'ExportFailed' => 'Export Failed', @@ -329,8 +349,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Execute command on all matches', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Message details of all matches', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'フィルター Px', 'FilterUnset' => 'You must specify a filter width and height', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Upload all matches', 'FilterVideoEvents' => 'Create video for all matches', 'Filters' => 'Filters', @@ -351,10 +373,11 @@ $SLANG = array( 'FrameRate' => 'フレームレート', 'FrameSkip' => 'フレームスキップ', 'Frames' => 'フレーム', - 'Func' => '機能\', - 'Function' => '機能\', + 'Func' => '機能', + 'Function' => '機能', 'Gain' => 'Gain', 'General' => 'General', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'ビデオの生成', 'GeneratingVideo' => 'ビデオ生成中', 'GoToZoneMinder' => 'ZoneMinder.comに行く', @@ -375,6 +398,7 @@ $SLANG = array( 'High' => '高', 'HighBW' => '高帯域', 'Home' => 'Home', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => '時', 'Hue' => '色相', 'Id' => 'ID', @@ -399,6 +423,7 @@ $SLANG = array( 'Line' => 'Line', // Added - 2011-06-16 'LinkedMonitors' => 'Linked Monitors', 'List' => 'List', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Load', 'Local' => 'ローカル', 'Log' => 'Log', // Added - 2011-06-16 @@ -485,6 +510,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 'Monitors' => 'モニター', 'Montage' => 'モンタージュ', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => '月', 'More' => 'More', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -511,6 +537,7 @@ $SLANG = array( 'Next' => '次', 'No' => 'いいえ', 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'このイベントのフレームは登録されていません', 'NoGroup' => 'No Group', 'NoSavedFilters' => '保存されたフィルターはありません', @@ -529,6 +556,8 @@ $SLANG = array( 'OpGt' => '以下', 'OpGtEq' => '同等か以上', 'OpIn' => 'セットに入っている', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => '以下', 'OpLtEq' => '同等か以下', 'OpMatches' => '一致する', @@ -538,6 +567,7 @@ $SLANG = array( 'Open' => 'Open', 'OptionHelp' => 'オプション ヘルプ', 'OptionRestartWarning' => 'この変更は起動中反映されない場合があります。\n変更してからZoneMinderを再起動してください。', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'オプション', 'OrEnterNewName' => '又は新しい名前を入力してください', 'Order' => 'Order', @@ -575,9 +605,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'レート', 'Real' => '生中継', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => '録画', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'イメージ ブレンド 参照 %', 'Refresh' => '最新の情報に更新', 'Remote' => 'リモート', @@ -593,6 +627,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Reset', 'ResetEventCounts' => 'イベント カウント リセット', 'Restart' => '再起動', @@ -611,6 +646,7 @@ $SLANG = array( 'Save' => '保存', 'SaveAs' => '名前をつけて保存', 'SaveFilter' => 'フィルターを保存', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'スケール', 'Score' => 'スコアー', 'Secs' => '秒', @@ -627,6 +663,7 @@ $SLANG = array( 'ShowFilterWindow' => 'フィルター ウインドーの表示', 'ShowTimeline' => 'Show Timeline', 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Size', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 'Sleep' => 'Sleep', @@ -646,6 +683,10 @@ $SLANG = array( 'State' => '状態', 'Stats' => '統計', 'Status' => '状態', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Step', 'StepBack' => 'Step Back', 'StepForward' => 'Step Forward', @@ -656,6 +697,8 @@ $SLANG = array( 'Stills' => 'スチール画像', 'Stop' => '停止', 'Stopped' => '停止状態', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'ストリーム', 'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Submit' => 'Submit', @@ -675,6 +718,7 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => 'タイムスタンプ', 'TimestampLabelFormat' => 'タイムスタンプ ラベル フォーマット', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'タイムスタンプ ラベル X', 'TimestampLabelY' => 'タイムスタンプ ラベル Y', 'Today' => 'Today', @@ -721,6 +765,7 @@ $SLANG = array( 'VideoGenParms' => 'ビデオ生成 パラメータ', 'VideoGenSucceeded' => 'Video Generation Succeeded!', 'VideoSize' => 'ビデオ サイズ', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => '表示', 'ViewAll' => '全部表示', 'ViewEvent' => 'View Event', @@ -730,6 +775,7 @@ $SLANG = array( 'Watch' => '監視', 'Web' => 'ウェブ', 'WebColour' => 'Web Colour', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => '週', 'White' => 'White', 'WhiteBalance' => 'White Balance', @@ -767,7 +813,7 @@ $CLANG = array( 'LastEvents' => '最終 %1$s %2$s', 'LatestRelease' => '最新バージョンは v%1$s、ご利用バージョンはv%2$s.', 'MonitorCount' => '%1$s %2$s', - 'MonitorFunction' => 'モニター%1$s 機能\', + 'MonitorFunction' => 'モニター%1$s 機能', 'RunningRecentVer' => 'あなたはZoneMinderの最新バージョン v%s.を使っています', 'VersionMismatch' => 'Version mismatch, system is version %1$s, database is %2$s.', // Added - 2011-05-25 ); diff --git a/web/lang/nl_nl.php b/web/lang/nl_nl.php index fca70a829..be741a6ac 100644 --- a/web/lang/nl_nl.php +++ b/web/lang/nl_nl.php @@ -79,6 +79,8 @@ $SLANG = array( 'Actual' => 'Origineel', 'AddNewControl' => 'Nieuwe controle toevoegen', 'AddNewMonitor' => 'Nieuwe monitor toevoegen', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Nieuwe gebruiker toevoegen', 'AddNewZone' => 'Nieuw gebied toevoegen', 'Alarm' => 'Alarm', @@ -106,22 +108,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Archiefstatus', 'AttrAvgScore' => 'Gem. score', 'AttrCause' => 'Oorzaak', - 'AttrDate' => 'Datum', - 'AttrDateTime' => 'Datum/tijd', 'AttrDiskBlocks' => 'Disk Blocks', 'AttrDiskPercent' => 'Disk Percent', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Duur', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Frames', 'AttrId' => 'Id', 'AttrMaxScore' => 'Max. Score', 'AttrMonitorId' => 'Monitor Id', 'AttrMonitorName' => 'Monitor Naam', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Naam', 'AttrNotes' => 'Notities', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'Systembelasting', - 'AttrTime' => 'Tijd', 'AttrTotalScore' => 'Totale Score', - 'AttrWeekday' => 'Weekdag', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Auto Stop Timeout', 'Available' => 'Beschikbaar', @@ -154,9 +166,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Reference blend percentage moet een geldige waarde van nul of groter zijn', 'BadSectionLength' => 'Sectielengte moet een getal van 30 of groter zijn', 'BadSignalCheckColour' => 'Signaalcontrolekleur moet een geldige RGB waarde zijn', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Stream replay buffer moet een geldige waarde van nul of groter zijn', 'BadWarmupCount' => 'Opwarm frames moet een geldig getal van nul of groter zijn', 'BadWebColour' => 'Webkleur moet een geldige webkleurwaarde bevatten', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Breedte moet een geldige waarde zijn', 'Bandwidth' => 'Bandbreedte', 'BandwidthHead' => 'bandbreedte', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -192,6 +206,7 @@ $SLANG = array( 'CanMoveRel' => 'Can Move Relatief', 'CanPan' => 'Can Pan' , 'CanReset' => 'Can Reset', + 'CanReboot' => 'Can Reboot', 'CanSetPresets' => 'Can Set Presets', 'CanSleep' => 'Can Sleep', 'CanTilt' => 'Can Tilt', @@ -220,10 +235,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Kies een logselectie', 'ChoosePreset' => 'Kies voorkeur', 'Clear' => 'Legen', + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Sluiten', 'Colour' => 'Kleur', 'Command' => 'Commando', 'Component' => 'Component', + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Configuratie', 'ConfiguredFor' => 'Geconfigureerd voor', 'ConfirmDeleteEvents' => 'Weet u zeker dat u deze gebeurtenissen wilt verwijderen?', @@ -273,7 +290,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18 'Donate' => 'Geef a.u.b. een donatie', 'DonateAlready' => 'Nee, ik heb al gedoneerd', - 'DonateEnticement' => 'U gebruikt ZoneMinder nu voor een geruime tijd, hopelijk vindt u het een nuttige toevoeging voor uw huis- of werkplekbeveiliging. Natuurlijk is en blijft ZoneMinder gratis en open source software, maar het kost geld om te ontwikkelen, ondersteunen, en te onderhouden. Wij vragen u dan ook om er over na te denken een donatie te doen om zo de ontwikkeling van ZoneMinder te ondersteunen. Natuurlijk bent u hier vrij in, en elke donatie hoe klein dan ook wordt erg gewaardeerd.

Als u wilt doneren geef dat hieronder dan aan of ga naar http://www.zoneminder.com/donate.html in uw browser.

Bedankt voor het gebruiken van ZoneMinder en vergeet niet om ons forum op ZoneMinder.com te bezoeken voor ondersteuning of suggesties waarmee uw ZoneMinder beleving nog beter wordt.', + 'DonateEnticement' => 'U gebruikt ZoneMinder nu voor een geruime tijd, hopelijk vindt u het een nuttige toevoeging voor uw huis- of werkplekbeveiliging. Natuurlijk is en blijft ZoneMinder gratis en open source software, maar het kost geld om te ontwikkelen, ondersteunen, en te onderhouden. Wij vragen u dan ook om er over na te denken een donatie te doen om zo de ontwikkeling van ZoneMinder te ondersteunen. Natuurlijk bent u hier vrij in, en elke donatie hoe klein dan ook wordt erg gewaardeerd.

Als u wilt doneren geef dat hieronder dan aan of ga naar https://zoneminder.com/donate/ in uw browser.

Bedankt voor het gebruiken van ZoneMinder en vergeet niet om ons forum op ZoneMinder.com te bezoeken voor ondersteuning of suggesties waarmee uw ZoneMinder beleving nog beter wordt.', 'DonateRemindDay' => 'Nu niet, herinner mij over 1 dag hieraan', 'DonateRemindHour' => 'Nu niet, herinner mij over een uur hieraan', 'DonateRemindMonth' => 'Nu niet, herinner mij over een maand hieraan', @@ -281,9 +298,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Nu niet, herinner mij over een week hieraan', 'DonateYes' => 'Ja, ik wil nu doneren', 'Download' => 'Downloaden', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Kopieer monitornaam', 'Duration' => 'Duur', 'Edit' => 'Bewerken', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Alarmen inschakelen', 'Enabled' => 'Ingeschakeld', @@ -300,6 +319,7 @@ $SLANG = array( 'Events' => 'Gebeurtenissen', 'Exclude' => 'Uitsluiten', 'Execute' => 'Uitvoeren', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Exporteren', 'ExportDetails' => 'Exporteer gebeurtenisdetails', 'ExportFailed' => 'Exporteren mislukt', @@ -329,8 +349,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Voer opdrachten uit op alle overeenkomsten', 'FilterLog' => 'Filterlog', 'FilterMessageEvents' => 'Bericht de details van alle overeenkomsten', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filter px', 'FilterUnset' => 'Je moet de filterhoogte en -breedte opgeven', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Verstuur alle overeenkomsten', 'FilterVideoEvents' => 'Maak video voor alle overeenkomsten', 'Filters' => 'Filters', @@ -355,6 +377,7 @@ $SLANG = array( 'Function' => 'Functie', 'Gain' => 'Gain', 'General' => 'Algemeen', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Genereer Video', 'GeneratingVideo' => 'Video wordt gegenereerd', 'GoToZoneMinder' => 'Ga naar ZoneMinder.com', @@ -375,6 +398,7 @@ $SLANG = array( 'High' => 'Hoog', 'HighBW' => 'Hoog B/W', 'Home' => 'Home', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Uur', 'Hue' => 'Hue', 'Id' => 'Id', @@ -399,6 +423,7 @@ $SLANG = array( 'Line' => 'Lijn', 'LinkedMonitors' => 'Gekoppelde monitoren', 'List' => 'Lijst', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Systeemlast', 'Local' => 'Lokaal', 'Log' => 'Log', @@ -485,6 +510,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'Deze lijst toont gedeteerde analoge en netwerk cameras en of deze al in gebruik of beschikbaar zijn.

Selecteer de gewenste waarde uit de lijst hieronder.

Let op dat mogelijk niet alle cameras hier worden weergegeven en dat alle ingevoerde waarden voor de huidige monitor zullen worden overschreven.

', 'Monitors' => 'Monitoren', 'Montage' => 'Montage', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Maand', 'More' => 'Meer', 'MotionFrameSkip' => 'Motion Frame Skip', @@ -511,6 +537,7 @@ $SLANG = array( 'Next' => 'Volgende', 'No' => 'Nee', 'NoDetectedCameras' => 'Geen cameras gedetecteerd', + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Er zijn geen beelden opgenomen voor deze gebeurtenis', 'NoGroup' => 'Geen Groep', 'NoSavedFilters' => 'Geen Opgeslagen Filters', @@ -529,6 +556,8 @@ $SLANG = array( 'OpGt' => 'groter dan', 'OpGtEq' => 'groter dan of gelijk aan', 'OpIn' => 'in set', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'kleiner dan', 'OpLtEq' => 'kleiner dan of gelijk aan', 'OpMatches' => 'Komt overeen met', @@ -538,6 +567,7 @@ $SLANG = array( 'Open' => 'Open', 'OptionHelp' => 'OptieHelp', 'OptionRestartWarning' => 'Deze veranderingen worden niet\ndoorgevoerd als het systeem loopt.\nVergeet niet ZoneMinder te herstarten\nwanneer u klaar bent.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Opties', 'OrEnterNewName' => 'of voer een nieuwe naam in', 'Order' => 'Sorteren', @@ -575,9 +605,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Voortgang', 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Snelheid', 'Real' => 'Echte', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Record', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Referentie beeld blend percentage', 'Refresh' => 'Verversen', 'Remote' => 'Remote', @@ -593,6 +627,7 @@ $SLANG = array( 'ReplayAll' => 'Alle Gebeurtenissen', 'ReplayGapless' => 'Opvolgende Gebeurtenissen', 'ReplaySingle' => 'Enkele Gebeurtenis', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Resetten', 'ResetEventCounts' => 'Gebeurtenisteller resetten', 'Restart' => 'Herstart', @@ -611,6 +646,7 @@ $SLANG = array( 'Save' => 'Opslaan', 'SaveAs' => 'Opslaan als', 'SaveFilter' => 'Filter opslaan', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Schaal', 'Score' => 'Score', 'Secs' => 'Sec.', @@ -627,6 +663,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Toon Filtervenster', 'ShowTimeline' => 'Toon Tijdlijn', 'SignalCheckColour' => 'Signaalcontrolekleur', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Groote', 'SkinDescription' => 'Wijzig standaarduiterlijk voor deze computer', 'Sleep' => 'Slaap', @@ -646,6 +683,10 @@ $SLANG = array( 'State' => 'Status', 'Stats' => 'Stats', 'Status' => 'Status', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Stap', 'StepBack' => 'Stap Terug', 'StepForward' => 'Stap Vooruit', @@ -656,6 +697,8 @@ $SLANG = array( 'Stills' => 'Beelden', 'Stop' => 'Stoppen', 'Stopped' => 'Gestopt', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Stream', 'StreamReplayBuffer' => 'Stream Replay beeldbuffer', 'Submit' => 'Verzenden', @@ -675,6 +718,7 @@ $SLANG = array( 'TimelineTip4' => 'Gebruik de knoppen hieronder om uit te zoomen of voor- en achteruit te navigeren.', 'Timestamp' => 'Tijdstempel', 'TimestampLabelFormat' => 'Formaat tijdstempel', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'Tijdstempel X-positie', 'TimestampLabelY' => 'Tijdstempel Y-positie', 'Today' => 'Vandaag', @@ -721,6 +765,7 @@ $SLANG = array( 'VideoGenParms' => 'Videogeneratie Parameters', 'VideoGenSucceeded' => 'Videogeneratie voltooid!', 'VideoSize' => 'Videogrootte', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Bekijk', 'ViewAll' => 'Bekijk Alles', 'ViewEvent' => 'Bekijk Gebeurtenis', @@ -730,6 +775,7 @@ $SLANG = array( 'Watch' => 'Observeer', 'Web' => 'Web', 'WebColour' => 'Webkleur', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Week', 'White' => 'Wit', 'WhiteBalance' => 'Witbalans', diff --git a/web/lang/pl_pl.php b/web/lang/pl_pl.php index 325c2e1b9..a1e8bd27a 100644 --- a/web/lang/pl_pl.php +++ b/web/lang/pl_pl.php @@ -20,6 +20,7 @@ // // ZoneMinder Polish Translation by Robert Krysztof // 2016-08-25 Updated by Dawid Kasza > dawid.kasza@gmail.com +// 2019-06-05 Updated by GospoGied > adm_gospogied(at)poczta.fm // // Notes for Translators // 0. Get some credit, put your name in the line above (optional) @@ -61,7 +62,7 @@ header( "Content-Type: text/html; charset=utf-8" ); // Also be aware that changing the whole locale may affect some floating point or decimal // arithmetic in the database, if this is the case change only the individual locale areas // that don't affect this rather than all at once. See the examples below. -// Finally, depending on your setup, PHP may not enjoy have multiple locales in a shared +// Finally, depending on your setup, PHP may not enjoy have multiple locales in a shared // threaded environment, if you get funny errors it may be this. // // Examples @@ -87,27 +88,29 @@ define( "STRF_FMT_DATETIME_SHORTER", "%m.%d. %H:%M:%S" ); // Strftime shor // Simple String Replacements $SLANG = array( '24BitColour' => 'Kolor (24 bity)', - '32BitColour' => 'Kolor (32 bity)', // Added - 2011-06-15 + '32BitColour' => 'Kolor (32 bity)', '8BitGrey' => 'Cz/b (8 bitów)', 'Action' => 'Działanie:', 'Actual' => 'Domyślna', - 'AddNewControl' => 'Add New Control', + 'AddNewControl' => 'Dodaj nowy kontroler', 'AddNewMonitor' => 'Dodaj nowy monitor', + 'AddNewServer' => 'Dodaj nowy serwer', + 'AddNewStorage' => 'Dodaj nowy magazyn', 'AddNewUser' => 'Dodaj użytkownika', 'AddNewZone' => 'Dodaj nową strefę', 'Alarm' => 'Alarm', 'AlarmBrFrames' => 'Ramki
alarmowe', 'AlarmFrame' => 'Ramka alarmowa', - 'AlarmFrameCount' => 'Alarm Frame Count', + 'AlarmFrameCount' => 'Ilość ramek alarmowych', 'AlarmLimits' => 'Ograniczenia alarmu', - 'AlarmMaximumFPS' => 'Alarm Maximum FPS', + 'AlarmMaximumFPS' => 'Maksymalny FPS alarmu', 'AlarmPx' => 'Alarm Px', - 'AlarmRGBUnset' => 'You must set an alarm RGB colour', - 'AlarmRefImageBlendPct'=> 'Alarm Reference Image Blend %ge', // Added - 2015-04-18 + 'AlarmRGBUnset' => 'Musisz ustawić kolor RGB alarmu', + 'AlarmRefImageBlendPct'=> 'Mieszanie alarmu z obrazem referencyjnym %ge', 'Alert' => 'Gotowość', 'All' => 'Wszystko', - 'AnalysisFPS' => 'Analysis FPS', // Added - 2015-07-22 - 'AnalysisUpdateDelay' => 'Analysis Update Delay', // Added - 2015-07-23 + 'AnalysisFPS' => 'FPS analizy', + 'AnalysisUpdateDelay' => 'Opóźnienie aktualizacji analizy', 'Apply' => 'Zastosuj', 'ApplyingStateChange' => 'Zmieniam stan pracy', 'ArchArchived' => 'Tylko zarchiwizowane', @@ -120,160 +123,175 @@ $SLANG = array( 'AttrArchiveStatus' => 'Status archiwum', 'AttrAvgScore' => 'Śred. wynik', 'AttrCause' => 'Powód', - 'AttrDate' => 'Data', - 'AttrDateTime' => 'Data/Czas', 'AttrDiskBlocks' => 'Dysk Bloki', 'AttrDiskPercent' => 'Procent zajętości', + 'AttrDiskSpace' => 'Miejsce na dysku', 'AttrDuration' => 'Czas trwania', + 'AttrEndDate' => 'Końcowa data', + 'AttrEndDateTime' => 'Końcowa data/godzina', + 'AttrEndTime' => 'Końcowy czas', + 'AttrEndWeekday' => 'Końcowy tydzień', + 'AttrFilterServer' => 'Filtr serwera pracuje na', 'AttrFrames' => 'Klatki', - 'AttrId' => 'Id', + 'AttrId' => 'Nr', 'AttrMaxScore' => 'Maks. wynik', 'AttrMonitorId' => 'Nr monitora', 'AttrMonitorName' => 'Nazwa monitora', + 'AttrMonitorServer' => 'Monitor serwera pracuje na', 'AttrName' => 'Nazwa', - 'AttrNotes' => 'Notes', - 'AttrSystemLoad' => 'Obiążenie systemu', - 'AttrTime' => 'Czas', + 'AttrNotes' => 'Notatki', + 'AttrStartDate' => 'Początkowa data', + 'AttrStartDateTime' => 'Początkowa data/czas', + 'AttrStartTime' => 'Początkowy czas', + 'AttrStartWeekday' => 'Początkowy tydzień', + 'AttrStateId' => 'Stan działania', + 'AttrStorageArea' => 'Magazyn', + 'AttrStorageServer' => 'Serwer hostujący', + 'AttrSystemLoad' => 'Obciążenie systemu', 'AttrTotalScore' => 'Całkowity wynik', - 'AttrWeekday' => 'Dzień roboczy', 'Auto' => 'Auto', - 'AutoStopTimeout' => 'Auto Stop Timeout', - 'Available' => 'Dostępne', // Added - 2009-03-31 + 'AutoStopTimeout' => 'Czas automatycznego stopu', + 'Available' => 'Dostępne', 'AvgBrScore' => 'Śred.
wynik', 'Background' => 'Działa w tle', 'BackgroundFilter' => 'Uruchom filtr w tle', - 'BadAlarmFrameCount' => 'Alarm frame count must be an integer of one or more', - 'BadAlarmMaxFPS' => 'Alarm Maximum FPS must be a positive integer or floating point value', - 'BadAnalysisFPS' => 'Analysis FPS must be a positive integer or floating point value', // Added - 2015-07-22 - 'BadAnalysisUpdateDelay'=> 'Analysis update delay must be set to an integer of zero or more', // Added - 2015-07-23 - 'BadChannel' => 'Channel must be set to an integer of zero or more', - 'BadColours' => 'Target colour must be set to a valid value', // Added - 2011-06-15 - 'BadDevice' => 'Device must be set to a valid value', - 'BadFPSReportInterval' => 'FPS report interval buffer count must be an integer of 0 or more', - 'BadFormat' => 'Format must be set to an integer of zero or more', - 'BadFrameSkip' => 'Frame skip count must be an integer of zero or more', - 'BadHeight' => 'Height must be set to a valid value', - 'BadHost' => 'Host must be set to a valid ip address or hostname, do not include http://', - 'BadImageBufferCount' => 'Image buffer size must be an integer of 10 or more', - 'BadLabelX' => 'Label X co-ordinate must be set to an integer of zero or more', - 'BadLabelY' => 'Label Y co-ordinate must be set to an integer of zero or more', - 'BadMaxFPS' => 'Maximum FPS must be a positive integer or floating point value', - 'BadMotionFrameSkip' => 'Motion Frame skip count must be an integer of zero or more', + 'BadAlarmFrameCount' => 'Liczba ramek alarmowych musi być liczbą całkowitą jeden lub więcej', + 'BadAlarmMaxFPS' => 'Maks. FPS alarmu musi być dodatnią liczbą całkowitą lub zmiennoprzecinkową', + 'BadAnalysisFPS' => 'Analiza FPS musi być dodatnią liczbą całkowitą lub zmiennoprzecinkową', + 'BadAnalysisUpdateDelay'=> 'Opóźnienie aktualizacji analizy musi być ustawione na liczbę całkowitą równą zero lub więcej', + 'BadChannel' => 'Kanał musi być ustawiony na liczbę całkowitą równą zero lub więcej', + 'BadColours' => 'Kolor docelowy musi być ustawiony na prawidłową wartość', + 'BadDevice' => 'Urządzenie musi mieć poprawną wartość', + 'BadFPSReportInterval' => 'Liczba buforów interwału raportu FPS musi być liczbą całkowitą równą 0 lub więcej', + 'BadFormat' => 'Format musi być ustawiony na liczbę całkowitą równą zero lub więcej', + 'BadFrameSkip' => 'Liczba pomijanych ramek musi być liczbą całkowitą równą zero lub więcej', + 'BadHeight' => 'Wysokość musi być ustawiona na prawidłową wartość', + 'BadHost' => 'Host musi być ustawiony na prawidłowy adres IP lub nazwę hosta, nie dołączaj http://', + 'BadImageBufferCount' => 'Rozmiar bufora obrazu musi być liczbą całkowitą 10 lub większą', + 'BadLabelX' => 'Współrzędna X etykiety musi być ustawiona na liczbę całkowitą równą zero lub więceje', + 'BadLabelY' => 'Współrzędna Y etykiety musi być ustawiona na liczbę całkowitą równą zero lub więcej', + 'BadMaxFPS' => 'Maksymalna liczba klatek na sekundę musi być dodatnią liczbą całkowitą lub zmiennoprzecinkową', + 'BadMotionFrameSkip' => 'Liczba pomijanych ramek ruchu musi być liczbą całkowitą równą zero lub więcej', 'BadNameChars' => 'Nazwy mogą zawierać tylko litery, cyfry oraz myślnik i podkreślenie', - 'BadPalette' => 'Palette must be set to a valid value', // Added - 2009-03-31 - 'BadPath' => 'Path must be set to a valid value', - 'BadPort' => 'Port must be set to a valid number', - 'BadPostEventCount' => 'Post event image count must be an integer of zero or more', - 'BadPreEventCount' => 'Pre event image count must be at least zero, and less than image buffer size', - 'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer', - 'BadSectionLength' => 'Section length must be an integer of 30 or more', - 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', - 'BadStreamReplayBuffer'=> 'Stream replay buffer must be an integer of zero or more', - 'BadWarmupCount' => 'Warmup frames must be an integer of zero or more', - 'BadWebColour' => 'Web colour must be a valid web colour string', - 'BadWidth' => 'Width must be set to a valid value', + 'BadPalette' => 'Paleta musi mieć poprawną wartość', + 'BadPath' => 'Ścieżka musi mieć poprawną wartość', + 'BadPort' => 'Port musi być ustawiony na prawidłowy numer', + 'BadPostEventCount' => 'Liczba zdjęć po zdarzeniu musi być liczbą całkowitą równą zero lub więcej', + 'BadPreEventCount' => 'Liczba obrazów przed zdarzeniem musi wynosić co najmniej zero i mniej niż rozmiar bufora obrazu', + 'BadRefBlendPerc' => 'Procent mieszania z referencyjnym obrazem musi być dodatnią liczbą całkowitą', + 'BadSectionLength' => 'Długość sekcji musi być liczbą całkowitą 30 lub większą', + 'BadSignalCheckColour' => 'Kolor kontroli sygnału musi być prawidłowym ciągiem kolorów RGB', + 'BadSourceType' => 'Typ źródła \"Web Site\" wymaga ustawienia funkcji \"Monitorowanie\"', + 'BadStreamReplayBuffer'=> 'Bufor odtwarzania strumieniowego musi być liczbą całkowitą równą zero lub więcej', + 'BadWarmupCount' => 'Ramki rozgrzewające muszą być liczbą całkowitą równą zero lub więcej', + 'BadWebColour' => 'Kolor strony musi być prawidłowy dla strony web', + 'BadWebSitePath' => 'Wprowadź pełny adres URL strony, w tym prefiks http: // lub https: //.', + 'BadWidth' => 'Szerokość musi być ustawiona na poprawną wartość', 'Bandwidth' => 'Przepustowość', 'BandwidthHead' => 'Przepustowość', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing 'BlobPx' => 'Plamka Px', 'BlobSizes' => 'Rozmiary plamek', 'Blobs' => 'Plamki', 'Brightness' => 'Jaskrawość', - 'Buffer' => 'Bufor', // Added - 2015-04-18 + 'Buffer' => 'Bufor', 'Buffers' => 'Bufory', - 'CSSDescription' => 'Change the default css for this computer', // Added - 2015-04-18 - 'CanAutoFocus' => 'Can Auto Focus', - 'CanAutoGain' => 'Can Auto Gain', - 'CanAutoIris' => 'Can Auto Iris', - 'CanAutoWhite' => 'Can Auto White Bal.', - 'CanAutoZoom' => 'Can Auto Zoom', - 'CanFocus' => 'Can Focus', - 'CanFocusAbs' => 'Can Focus Absolute', - 'CanFocusCon' => 'Can Focus Continuous', - 'CanFocusRel' => 'Can Focus Relative', - 'CanGain' => 'Can Gain ', - 'CanGainAbs' => 'Can Gain Absolute', - 'CanGainCon' => 'Can Gain Continuous', - 'CanGainRel' => 'Can Gain Relative', - 'CanIris' => 'Can Iris', - 'CanIrisAbs' => 'Can Iris Absolute', - 'CanIrisCon' => 'Can Iris Continuous', - 'CanIrisRel' => 'Can Iris Relative', - 'CanMove' => 'Can Move', - 'CanMoveAbs' => 'Can Move Absolute', - 'CanMoveCon' => 'Can Move Continuous', - 'CanMoveDiag' => 'Can Move Diagonally', - 'CanMoveMap' => 'Can Move Mapped', - 'CanMoveRel' => 'Can Move Relative', - 'CanPan' => 'Can Pan' , - 'CanReset' => 'Can Reset', - 'CanSetPresets' => 'Can Set Presets', - 'CanSleep' => 'Can Sleep', - 'CanTilt' => 'Can Tilt', - 'CanWake' => 'Can Wake', - 'CanWhite' => 'Can White Balance', - 'CanWhiteAbs' => 'Can White Bal. Absolute', - 'CanWhiteBal' => 'Can White Bal.', - 'CanWhiteCon' => 'Can White Bal. Continuous', - 'CanWhiteRel' => 'Can White Bal. Relative', - 'CanZoom' => 'Can Zoom', - 'CanZoomAbs' => 'Can Zoom Absolute', - 'CanZoomCon' => 'Can Zoom Continuous', - 'CanZoomRel' => 'Can Zoom Relative', + 'CSSDescription' => 'Zmień domyślny css dla tego komputera', + 'CanAutoFocus' => 'Może auto. skupiać', + 'CanAutoGain' => 'Może auto. wzmacniać', + 'CanAutoIris' => 'Może auto. ust. ogniskową', + 'CanAutoWhite' => 'Może auto. ust. balans bieli', + 'CanAutoZoom' => 'Może auto. zbliżać', + 'CanFocus' => 'Może ust. ogniskową', + 'CanFocusAbs' => 'Może ust. ogniskową całkowicie', + 'CanFocusCon' => 'Może ust. ogniskową ciągle', + 'CanFocusRel' => 'Może ust. ogniskową relatywnie', + 'CanGain' => 'Może wzmacniać ', + 'CanGainAbs' => 'Może wzmacniać absolutnie', + 'CanGainCon' => 'Może wzmacniać ciągle', + 'CanGainRel' => 'Może wzmacniać relatywnie', + 'CanIris' => 'Może ust. ogniskową', + 'CanIrisAbs' => 'Może ust. ogniskową całkowicie', + 'CanIrisCon' => 'Może ust. ogniskową ciągle', + 'CanIrisRel' => 'Może ust. ogniskową relatywnie', + 'CanMove' => 'Można obracać', + 'CanMoveAbs' => 'Można obracać całkowicie', + 'CanMoveCon' => 'Można obracać ciągle', + 'CanMoveDiag' => 'Można obracać po przekątnej', + 'CanMoveMap' => 'Można obracać mapowanie', + 'CanMoveRel' => 'Można obracać relatywnie', + 'CanPan' => 'Można panoramę' , + 'CanReset' => 'Można resetować', + 'CanReboot' => 'Można restartować', + 'CanSetPresets' => 'Można ustawiać predefiniowana', + 'CanSleep' => 'Można usypiać', + 'CanTilt' => 'Można odchylać', + 'CanWake' => 'Można wybudzać', + 'CanWhite' => 'Może ust. balans bieli', + 'CanWhiteAbs' => 'Może ust. balans bieli całkowicie', + 'CanWhiteBal' => 'Może ust. balans bieli', + 'CanWhiteCon' => 'Może ust. balans bieli ciągle', + 'CanWhiteRel' => 'Może ust. balans bieli relatywnie', + 'CanZoom' => 'Można zbliżać', + 'CanZoomAbs' => 'Można zbliżać całkowicie', + 'CanZoomCon' => 'Można zbliżać ciągle', + 'CanZoomRel' => 'Można zbliżać relatywnie', 'Cancel' => 'Anuluj', 'CancelForcedAlarm' => 'Anuluj wymuszony alarm', 'CaptureHeight' => 'Wysokość obrazu', - 'CaptureMethod' => 'Metoda przechwytywania', // Added - 2009-02-08 + 'CaptureMethod' => 'Metoda przechwytywania', 'CapturePalette' => 'Paleta kolorów obrazu', - 'CaptureResolution' => 'Capture Resolution', // Added - 2015-04-18 + 'CaptureResolution' => 'Rozdzielczość nagrywania', 'CaptureWidth' => 'Szerokość obrazu', 'Cause' => 'Przyczyna', 'CheckMethod' => 'Metoda sprawdzenia alarmu', - 'ChooseDetectedCamera' => 'Choose Detected Camera', // Added - 2009-03-31 + 'ChooseDetectedCamera' => 'Wybierz wykrytą kamerę', 'ChooseFilter' => 'Wybierz filtr', - 'ChooseLogFormat' => 'Choose a log format', // Added - 2011-06-17 - 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 - 'ChoosePreset' => 'Choose Preset', - 'Clear' => 'Wyczyść', // Added - 2011-06-16 + 'ChooseLogFormat' => 'Wybierz format logów', + 'ChooseLogSelection' => 'Wybierz sposób wybierania logów', + 'ChoosePreset' => 'Wybierz ustawienie', + 'Clear' => 'Wyczyść', + 'CloneMonitor' => 'Klonuj', 'Close' => 'Zamknij', 'Colour' => 'Nasycenie', 'Command' => 'Polecenie', - 'Component' => 'Komponent', // Added - 2011-06-16 + 'Component' => 'Komponent', + 'ConcurrentFilter' => 'Uruchom filtr równolegle', 'Config' => 'Konfiguracja', 'ConfiguredFor' => 'Ustawiona', 'ConfirmDeleteEvents' => 'Jesteś pewien, że chcesz usunąć zaznaczone zdarzenia?', - 'ConfirmPassword' => 'PotwierdŹ hasło', + 'ConfirmPassword' => 'Potwierdź hasło', 'ConjAnd' => 'i', 'ConjOr' => 'lub', 'Console' => 'Konsola', - 'ContactAdmin' => 'Skontaktuj się z Twoim adminstratorem w sprawie szczegółów.', - 'Continue' => 'Continue', + 'ContactAdmin' => 'Skontaktuj się z Twoim administratorem w sprawie szczegółów.', + 'Continue' => 'Kontynuuj', 'Contrast' => 'Kontrast', - 'Control' => 'Control', - 'ControlAddress' => 'Control Address', - 'ControlCap' => 'Control Capability', - 'ControlCaps' => 'Control Capabilities', - 'ControlDevice' => 'Control Device', - 'ControlType' => 'Control Type', - 'Controllable' => 'Controllable', - 'Current' => 'Obecny', // Added - 2015-04-18 - 'Cycle' => 'Cykl', + 'Control' => 'Kontrola', + 'ControlAddress' => 'Adres sterowania', + 'ControlCap' => 'Możliwość sterowania', + 'ControlCaps' => 'Możliwość sterowania', + 'ControlDevice' => 'Kontrola urządzenia', + 'ControlType' => 'Typ sterowania', + 'Controllable' => 'Sterowana', + 'Current' => 'Obecny', + 'Cycle' => 'Podgląd cykliczny', 'CycleWatch' => 'Cykl podglądu', - 'DateTime' => 'Data/Czas', // Added - 2011-06-16 + 'DateTime' => 'Data/Czas', 'Day' => 'Dzień', 'Debug' => 'Debug', - 'DefaultRate' => 'Default Rate', + 'DefaultRate' => 'Domyślna szybkość', 'DefaultScale' => 'Skala domyślna', 'DefaultView' => 'Widok domyślny', - 'Deinterlacing' => 'Usuwanie przeplotu', // Added - 2015-04-18 - 'Delay' => 'Opóźnienie', // Added - 2015-04-18 + 'Deinterlacing' => 'Usuwanie przeplotu', + 'Delay' => 'Opóźnienie', 'Delete' => 'Usuń', 'DeleteAndNext' => 'Usuń & następny', 'DeleteAndPrev' => 'Usuń & poprzedni', 'DeleteSavedFilter' => 'Usuń zapisany filtr', 'Description' => 'Opis', - 'DetectedCameras' => 'Wykryte kamery', // Added - 2009-03-31 - 'DetectedProfiles' => 'Wykryte profile', // Added - 2015-04-18 - 'Device' => 'Urządzenie', // Added - 2009-02-08 + 'DetectedCameras' => 'Wykryte kamery', + 'DetectedProfiles' => 'Wykryte profile', + 'Device' => 'Urządzenie', 'DeviceChannel' => 'Numer wejścia w urządzeniu', 'DeviceFormat' => 'System TV', 'DeviceNumber' => 'Numer urządzenia', @@ -282,24 +300,26 @@ $SLANG = array( 'Dimensions' => 'Rozmiary', 'DisableAlarms' => 'Wyłącz alarm', 'Disk' => 'Dysk', - 'Display' => 'Wygląd', // Added - 2011-01-30 - 'Displaying' => 'Wyświetlanie', // Added - 2011-06-16 - 'DoNativeMotionDetection'=> 'Do Native Motion Detection', - 'Donate' => 'Please Donate', - 'DonateAlready' => 'No, I\'ve already donated', - 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', - 'DonateRemindDay' => 'Not yet, remind again in 1 day', - 'DonateRemindHour' => 'Not yet, remind again in 1 hour', - 'DonateRemindMonth' => 'Not yet, remind again in 1 month', - 'DonateRemindNever' => 'No, I don\'t want to donate, never remind', - 'DonateRemindWeek' => 'Not yet, remind again in 1 week', - 'DonateYes' => 'Yes, I\'d like to donate now', + 'Display' => 'Wygląd', + 'Displaying' => 'Wyświetlanie', + 'DoNativeMotionDetection'=> 'Wykonaj natywne wykrywanie ruchu', + 'Donate' => 'Proszę zrób darowiznę', + 'DonateAlready' => 'Nie, już wykonałem darowiznę', + 'DonateEnticement' => 'Używasz ZoneMinder już od jakiegoś czasu i mam nadzieję, że jest to przydatne uzupełnienie bezpieczeństwa w domu lub w miejscu pracy. Mimo że ZoneMinder jest i pozostanie darmowy i otwarty, to tworzenie go i wsparcie kosztuje. Jeśli chcesz wesprzeć przyszły rozwój i nowe funkcje, weź pod uwagę darowiznę. Darowizna jest oczywiście opcjonalna, ale bardzo ceniona i możesz przekazać darowizny tak dużo lub tak mało, jak chcesz.

Jeśli chcesz przekazać darowiznę, wybierz opcję poniżej lub przejdź do https://zoneminder.com/donate/ w przeglądarce.

Dziękujemy za korzystanie z ZoneMinder i nie zapomnij odwiedzić forów na ZoneMinder.com, aby uzyskać pomoc lub sugestie, jak sprawić, by korzystanie z ZoneMinder było jeszcze lepsze.', + 'DonateRemindDay' => 'Jeszcze nie, przypomnij za 1 dzień', + 'DonateRemindHour' => 'Jeszcze nie, przypomnij za 1 godzinę', + 'DonateRemindMonth' => 'Jeszcze nie, przypomnij za 1 miesiąc', + 'DonateRemindNever' => 'Nie, nie chcę wykonać darowizny, nigdy nie przypominaj', + 'DonateRemindWeek' => 'Jeszcze nie, przypomnij za 1 tydzień', + 'DonateYes' => 'Tak, chcę wykonać darowiznę teraz', 'Download' => 'Pobierz', - 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 + 'DownloadVideo' => 'Pobierz wideo', + 'DuplicateMonitorName' => 'Duplikuj nazwę ', 'Duration' => 'Czas trwania', 'Edit' => 'Edycja', + 'EditLayout' => 'Edytuj układ', 'Email' => 'Email', - 'EnableAlarms' => 'Enable Alarms', + 'EnableAlarms' => 'Włącz alarmy', 'Enabled' => 'Aktywny', 'EnterNewFilterName' => 'Wpisz nową nazwę filtra', 'Error' => 'Błąd', @@ -308,87 +328,92 @@ $SLANG = array( 'Etc' => 'itp', 'Event' => 'Zdarzenie', 'EventFilter' => 'Filtr zdarzeń', - 'EventId' => 'Id zdarzenia', - 'EventName' => 'Event Name', - 'EventPrefix' => 'Event Prefix', + 'EventId' => 'Nr zdarzenia', + 'EventName' => 'Nazwa zdarzenia', + 'EventPrefix' => 'Prefiks zdarzenia', 'Events' => 'Zdarzenia', 'Exclude' => 'Wyklucz', 'Execute' => 'Wykonaj', + 'Exif' => 'Zapisz dane EXIF do obrazu', 'Export' => 'Eksport', 'ExportDetails' => 'Eksport szczegółów zdarzenia', 'ExportFailed' => 'Eksport nie powiódł się', 'ExportFormat' => 'Rodzaj archiwum', 'ExportFormatTar' => 'Tar', 'ExportFormatZip' => 'Zip', - 'ExportFrames' => 'Eksport szczgółów klatki', + 'ExportFrames' => 'Eksport szczegółów klatki', 'ExportImageFiles' => 'Eksport plików obrazowych (klatek)', - 'ExportLog' => 'Eksport logów', // Added - 2011-06-17 + 'ExportLog' => 'Eksport logów', 'ExportMiscFiles' => 'Eksport innych plików (jeśli dostępne)', 'ExportOptions' => 'Opcje eksportu', - 'ExportSucceeded' => 'Eksport zakończony pomyślnie', // Added - 2009-02-08 + 'ExportSucceeded' => 'Eksport zakończony pomyślnie', 'ExportVideoFiles' => 'Eksport plików video (jeśli dostępne)', 'Exporting' => 'Eksportowanie', 'FPS' => 'fps', 'FPSReportInterval' => 'Raport (ramek/s)', 'FTP' => 'FTP', - 'Far' => 'Far', - 'FastForward' => 'Fast Forward', + 'Far' => 'Daleko', + 'FastForward' => 'Szybkie przewijanie', 'Feed' => 'Dostarcz', - 'Ffmpeg' => 'Ffmpeg', // Added - 2009-02-08 + 'Ffmpeg' => 'FFmpeg', 'File' => 'Plik', - 'Filter' => 'Filter', // Added - 2015-04-18 + 'Filter' => 'Filtr', 'FilterArchiveEvents' => 'Archiwizuj wszystkie pasujące', - 'FilterDeleteEvents' => 'Usuwaj wszystkie pasujące', + 'FilterDeleteEvents' => 'Usuń wszystkie pasujące', 'FilterEmailEvents' => 'Wysyłaj pocztą wszystkie pasujące', 'FilterExecuteEvents' => 'Wywołuj komendę dla wszystkich pasujących', - 'FilterLog' => 'Filtr logów', // Added - 2015-04-18 + 'FilterLog' => 'Filtr logów', 'FilterMessageEvents' => 'Wyświetlaj komunikat na wszystkie pasujące', + 'FilterMoveEvents' => 'Przenieś wszystkie pasujące', 'FilterPx' => 'Filtr Px', - 'FilterUnset' => 'You must specify a filter width and height', + 'FilterUnset' => 'Musisz określić szerokość i wysokość filtra', + 'FilterUpdateDiskSpace'=> 'Zaktualizuj zajętość dysku', 'FilterUploadEvents' => 'Wysyłaj wszystkie pasujące', 'FilterVideoEvents' => 'Utwórz nagranie dla zaznaczonych', 'Filters' => 'Filtry', 'First' => 'Pierwszy', 'FlippedHori' => 'Odwróć poziomo', 'FlippedVert' => 'Odwróć pionowo', - 'FnMocord' => 'Mocord', // Added 2013.08.16. - 'FnModect' => 'Modect', // Added 2013.08.16. - 'FnMonitor' => 'Monitor', // Added 2013.08.16. - 'FnNodect' => 'Nodect', // Added 2013.08.16. - 'FnNone' => 'None', // Added 2013.08.16. - 'FnRecord' => 'Record', // Added 2013.08.16. - 'Focus' => 'Focus', + 'FnMocord' => 'Wykr. ruchu z nagrywaniem', + 'FnModect' => 'Wykr. ruchu', + 'FnMonitor' => 'Monitorowanie', + 'FnNodect' => 'Zew. zdarzania', + 'FnNone' => 'Wyłączony', + 'FnRecord' => 'Nagrywanie', + 'Focus' => 'Skupienie', 'ForceAlarm' => 'Wymuś alarm', 'Format' => 'Format', 'Frame' => 'Ramka', - 'FrameId' => 'Nr ramki', + 'FrameId' => 'Nr. ramki', 'FrameRate' => 'Tempo ramek', 'FrameSkip' => 'Pomiń ramkę', 'Frames' => 'Ramki', 'Func' => 'Funkcja', 'Function' => 'Funkcja', - 'Gain' => 'Gain', - 'General' => 'General', - 'GenerateVideo' => 'Generowanie Video', - 'GeneratingVideo' => 'Generuję Video', - 'GoToZoneMinder' => 'PrzejdŹ na ZoneMinder.com', + 'Gain' => 'Wzmocnienie', + 'General' => 'Ogólne', + 'GenerateDownload' => 'Generowanie pobierania', + 'GenerateVideo' => 'Generowanie wideo', + 'GeneratingVideo' => 'Generuję wideo', + 'GoToZoneMinder' => 'Przejdź na ZoneMinder.com', 'Grey' => 'Cz/b', 'Group' => 'Grupa', 'Groups' => 'Grupy', - 'HasFocusSpeed' => 'Has Focus Speed', - 'HasGainSpeed' => 'Has Gain Speed', - 'HasHomePreset' => 'Has Home Preset', - 'HasIrisSpeed' => 'Has Iris Speed', - 'HasPanSpeed' => 'Has Pan Speed', - 'HasPresets' => 'Has Presets', - 'HasTiltSpeed' => 'Has Tilt Speed', - 'HasTurboPan' => 'Has Turbo Pan', - 'HasTurboTilt' => 'Has Turbo Tilt', - 'HasWhiteSpeed' => 'Has White Bal. Speed', - 'HasZoomSpeed' => 'Has Zoom Speed', - 'High' => 'wysoka', + 'HasFocusSpeed' => 'Ma prędkość skupiania', + 'HasGainSpeed' => 'Ma prędkość wzmocnienia', + 'HasHomePreset' => 'Ma ustawienia początkowej pozycji', + 'HasIrisSpeed' => 'Ma prędkość ust. ogniskowej', + 'HasPanSpeed' => 'Ma prędkość panoramy', + 'HasPresets' => 'Ma ustawienia predefiniowane', + 'HasTiltSpeed' => 'Ma prędkość odchylania', + 'HasTurboPan' => 'Ma turbo panoramę', + 'HasTurboTilt' => 'Ma turbo odchylanie', + 'HasWhiteSpeed' => 'Ma prędkość balansu bieli', + 'HasZoomSpeed' => 'Ma prędkość zbliżenia', + 'High' => 'Wysokość', 'HighBW' => 'Wys. prz.', - 'Home' => 'Home', + 'Home' => 'Początkowa pozycja', + 'Hostname' => 'Nazwa hosta', 'Hour' => 'Godzina', 'Hue' => 'Odcień', 'Id' => 'Nr', @@ -400,121 +425,123 @@ $SLANG = array( 'In' => 'In', 'Include' => 'Dołącz', 'Inverted' => 'Odwrócony', - 'Iris' => 'Iris', - 'KeyString' => 'Key String', - 'Label' => 'Label', + 'Iris' => 'Ogniskowa', + 'KeyString' => 'Łańcuch klucza', + 'Label' => 'Etykieta', 'Language' => 'Język', 'Last' => 'Ostatni', - 'Layout' => 'Layout', // Added - 2009-02-08 - 'Level' => 'Level', // Added - 2011-06-16 + 'Layout' => 'Układ', + 'Level' => 'Poziom', 'Libvlc' => 'Libvlc', 'LimitResultsPost' => 'wyników;', // This is used at the end of the phrase 'Limit to first N results only' 'LimitResultsPre' => 'Ogranicz do początkowych', // This is used at the beginning of the phrase 'Limit to first N results only' - 'Line' => 'Linia', // Added - 2011-06-16 + 'Line' => 'Linia', 'LinkedMonitors' => 'Połączone monitory', 'List' => 'Lista', + 'ListMatches' => 'Pokaż pasujące', 'Load' => 'Obc.', 'Local' => 'Lokalny', - 'Log' => 'Logi', // Added - 2011-06-16 + 'Log' => 'Logi', 'LoggedInAs' => 'Zalogowany jako', - 'Logging' => 'Logowanie', // Added - 2011-06-16 + 'Logging' => 'Logowanie', 'LoggingIn' => 'Logowanie', 'Login' => 'Login', 'Logout' => 'Wyloguj', - 'Logs' => 'Logi', // Added - 2011-06-17 + 'Logs' => 'Logi', 'Low' => 'niska', 'LowBW' => 'Nis. prz.', - 'Main' => 'Main', - 'Man' => 'Man', - 'Manual' => 'Manual', + 'Main' => 'Główny', + 'Man' => 'Podr.', + 'Manual' => 'Podręcznik', 'Mark' => 'Znacznik', 'Max' => 'Maks.', - 'MaxBandwidth' => 'Max Bandwidth', + 'MaxBandwidth' => 'Maks. przepustowość', 'MaxBrScore' => 'Maks.
wynik', - 'MaxFocusRange' => 'Max Focus Range', - 'MaxFocusSpeed' => 'Max Focus Speed', - 'MaxFocusStep' => 'Max Focus Step', - 'MaxGainRange' => 'Max Gain Range', - 'MaxGainSpeed' => 'Max Gain Speed', - 'MaxGainStep' => 'Max Gain Step', - 'MaxIrisRange' => 'Max Iris Range', - 'MaxIrisSpeed' => 'Max Iris Speed', - 'MaxIrisStep' => 'Max Iris Step', - 'MaxPanRange' => 'Max Pan Range', - 'MaxPanSpeed' => 'Max Pan Speed', - 'MaxPanStep' => 'Max Pan Step', - 'MaxTiltRange' => 'Max Tilt Range', - 'MaxTiltSpeed' => 'Max Tilt Speed', - 'MaxTiltStep' => 'Max Tilt Step', - 'MaxWhiteRange' => 'Max White Bal. Range', - 'MaxWhiteSpeed' => 'Max White Bal. Speed', - 'MaxWhiteStep' => 'Max White Bal. Step', - 'MaxZoomRange' => 'Max Zoom Range', - 'MaxZoomSpeed' => 'Max Zoom Speed', - 'MaxZoomStep' => 'Max Zoom Step', + 'MaxFocusRange' => 'Maks. zakres skupiania', + 'MaxFocusSpeed' => 'Maks. prędkość skupiania', + 'MaxFocusStep' => 'Maks. krok skupiania', + 'MaxGainRange' => 'Maks. zakres wzmocnienia', + 'MaxGainSpeed' => 'Maks. prędkość wzmocnienia', + 'MaxGainStep' => 'Maks. krok wzmocnienia', + 'MaxIrisRange' => 'Maks. zakres ust. ogniskowej', + 'MaxIrisSpeed' => 'Maks. prędkość ust. ogniskowej', + 'MaxIrisStep' => 'Maks. krok ust. ogniskowej', + 'MaxPanRange' => 'Maks. zakres panoramy', + 'MaxPanSpeed' => 'Maks. prędkość panoramy', + 'MaxPanStep' => 'Maks. krok panoramy', + 'MaxTiltRange' => 'Maks. zakres odchylania', + 'MaxTiltSpeed' => 'Maks. prędkość odchylania', + 'MaxTiltStep' => 'Maks. krok odchylania', + 'MaxWhiteRange' => 'Maks. zakres balansu bieli', + 'MaxWhiteSpeed' => 'Maks. prędkość balansu bieli', + 'MaxWhiteStep' => 'Maks. krok balansu bieli', + 'MaxZoomRange' => 'Maks. zakres zbliżenia', + 'MaxZoomSpeed' => 'Maks. prędkość zbliżenia', + 'MaxZoomStep' => 'Maks. krok zbliżenia', 'MaximumFPS' => 'Maks. FPS', 'Medium' => 'średnia', 'MediumBW' => 'Śred. prz.', - 'Message' => 'Treść', // Added - 2011-06-16 - 'MinAlarmAreaLtMax' => 'Minimum alarm area should be less than maximum', - 'MinAlarmAreaUnset' => 'You must specify the minimum alarm pixel count', + 'Message' => 'Treść', + 'MinAlarmAreaLtMax' => 'Minimalna powierzchnia alarmu powinna być mniejsza niż maksymalna', + 'MinAlarmAreaUnset' => 'Musisz określić minimalną liczbę pikseli alarmu', 'MinBlobAreaLtMax' => 'Minimalny obszar plamki powinien być mniejszy od maksymalnego obszaru plamki', - 'MinBlobAreaUnset' => 'You must specify the minimum blob pixel count', - 'MinBlobLtMinFilter' => 'Minimum blob area should be less than or equal to minimum filter area', + 'MinBlobAreaUnset' => 'Musisz określić minimalną liczbę pikseli plamki', + 'MinBlobLtMinFilter' => 'Minimalna powierzchnia plamki powinna być mniejsza lub równa minimalnej powierzchni filtra', 'MinBlobsLtMax' => 'Najmniejsze plamki powinny być mniejsze od największych plamek' , - 'MinBlobsUnset' => 'You must specify the minimum blob count', - 'MinFilterAreaLtMax' => 'Minimum filter area should be less than maximum', - 'MinFilterAreaUnset' => 'You must specify the minimum filter pixel count', - 'MinFilterLtMinAlarm' => 'Minimum filter area should be less than or equal to minimum alarm area', - 'MinFocusRange' => 'Min Focus Range', - 'MinFocusSpeed' => 'Min Focus Speed', - 'MinFocusStep' => 'Min Focus Step', - 'MinGainRange' => 'Min Gain Range', - 'MinGainSpeed' => 'Min Gain Speed', - 'MinGainStep' => 'Min Gain Step', - 'MinIrisRange' => 'Min Iris Range', - 'MinIrisSpeed' => 'Min Iris Speed', - 'MinIrisStep' => 'Min Iris Step', - 'MinPanRange' => 'Min Pan Range', - 'MinPanSpeed' => 'Min Pan Speed', - 'MinPanStep' => 'Min Pan Step', + 'MinBlobsUnset' => 'Musisz określić minimalną liczbę plamek', + 'MinFilterAreaLtMax' => 'Minimalna powierzchnia filtra powinna być mniejsza niż maksymalna', + 'MinFilterAreaUnset' => 'Musisz określić minimalną liczbę pikseli filtra', + 'MinFilterLtMinAlarm' => 'Minimalna powierzchnia filtra powinna być mniejsza lub równa minimalnej powierzchni alarmu', + 'MinFocusRange' => 'Min. zakres skupiania', + 'MinFocusSpeed' => 'Min. prędkość skupiania', + 'MinFocusStep' => 'Min. krok skupiania', + 'MinGainRange' => 'Min. zakres wzmocnienia', + 'MinGainSpeed' => 'Min. prędkość wzmocnienia', + 'MinGainStep' => 'Min. krok wzmocnienia', + 'MinIrisRange' => 'Min. zakres ust. ogniskowej', + 'MinIrisSpeed' => 'Min. prędkość ust. ogniskowej', + 'MinIrisStep' => 'Min. krok ust. ogniskowej', + 'MinPanRange' => 'Min. zakres panoramy', + 'MinPanSpeed' => 'Min. prędkość panoramy', + 'MinPanStep' => 'Min. krok panoramy', 'MinPixelThresLtMax' => 'Najmniejsze progi pikseli powinny być mniejsze od największych progów pikseli', - 'MinPixelThresUnset' => 'You must specify a minimum pixel threshold', - 'MinTiltRange' => 'Min Tilt Range', - 'MinTiltSpeed' => 'Min Tilt Speed', - 'MinTiltStep' => 'Min Tilt Step', - 'MinWhiteRange' => 'Min White Bal. Range', - 'MinWhiteSpeed' => 'Min White Bal. Speed', - 'MinWhiteStep' => 'Min White Bal. Step', - 'MinZoomRange' => 'Min Zoom Range', - 'MinZoomSpeed' => 'Min Zoom Speed', - 'MinZoomStep' => 'Min Zoom Step', + 'MinPixelThresUnset' => 'Musisz określić minimalny próg pikseli', + 'MinTiltRange' => 'Min zakres odchylania', + 'MinTiltSpeed' => 'Min prędkość odchylania', + 'MinTiltStep' => 'Min krok odchylania', + 'MinWhiteRange' => 'Min zakres balansu bieli', + 'MinWhiteSpeed' => 'Min prędkość balansu bieli', + 'MinWhiteStep' => 'Min krok balansu bieli', + 'MinZoomRange' => 'Min zakres zbliżenia', + 'MinZoomSpeed' => 'Min prędkość zbliżenia', + 'MinZoomStep' => 'Min krok zbliżenia', 'Misc' => 'Inne', - 'Mode' => 'Tryb', // Added - 2015-04-18 + 'Mode' => 'Tryb', 'Monitor' => 'Monitor', - 'MonitorIds' => 'Numery monitorów', - 'MonitorPreset' => 'Ustawienia predefiniowane', - 'MonitorPresetIntro' => 'Select an appropriate preset from the list below.

Please note that this may overwrite any values you already have configured for this monitor.

', - 'MonitorProbe' => 'Monitor Probe', // Added - 2009-03-31 - 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 + 'MonitorIds' => 'Numery monitorów', + 'MonitorPreset' => 'Predefiniowane ustawienia ', + 'MonitorPresetIntro' => 'Wybierz odpowiednie ustawienie wstępne z poniższej listy.

Pamiętaj, że może to zastąpić wszystkie wartości skonfigurowane dla tego monitora.

', + 'MonitorProbe' => 'Predefiniowane ustawienia monitorów', + 'MonitorProbeIntro' => 'Poniższa lista pokazuje wykryte kamery analogowe i sieciowe oraz informację, czy są one już używane lub dostępne do wyboru.

Wybierz żądany wpis z poniższej listy.

Należy pamiętać, że nie wszystkie kamery mogły zostać wykryte i wybór tutaj kamery może zastąpić wszystkie wartości skonfigurowane dla bieżącego monitora.

', 'Monitors' => 'Monitory', - 'Montage' => 'Montaż', + 'Montage' => 'Podgląd wszystich kamer na raz', + 'MontageReview' => 'Podgląd wszystich kamer Alternatywny', 'Month' => 'Miesiąc', - 'More' => 'Pokaż więcej', // Added - 2011-06-16 - 'MotionFrameSkip' => 'Motion Frame Skip', - 'Move' => 'Move', - 'Mtg2widgrd' => '2-wide grid', // Added 2013.08.15. - 'Mtg3widgrd' => '3-wide grid', // Added 2013.08.15. - 'Mtg3widgrx' => '3-wide grid, scaled, enlarge on alarm', // Added 2013.08.15. - 'Mtg4widgrd' => '4-wide grid', // Added 2013.08.15. - 'MtgDefault' => 'Default', // Added 2013.08.15. + 'More' => 'Pokaż więcej', + 'MotionFrameSkip' => 'Pomijanie ramek wykrycia ruchu', + 'Move' => 'Przesuń', + 'Mtg2widgrd' => '2-kolumnowa siatka', + 'Mtg3widgrd' => '3-kolumnowa siatka', + 'Mtg3widgrx' => '3-kolumnowa siatka, skalowana, powiększana na alarm', + 'Mtg4widgrd' => '4-kolumnowa siatka', + 'MtgDefault' => 'Domyślny', 'MustBeGe' => 'musi być większe lub równe od', 'MustBeLe' => 'musi być mniejsze lub równe od', 'MustConfirmPassword' => 'Musisz potwierdzić hasło', 'MustSupplyPassword' => 'Musisz podać hasło', 'MustSupplyUsername' => 'Musisz podać nazwę użytkownika', 'Name' => 'Nazwa', - 'Near' => 'Near', + 'Near' => 'W pobliżu', 'Network' => 'Sieć', 'New' => 'Nowy', 'NewGroup' => 'Nowa grupa', @@ -524,25 +551,28 @@ $SLANG = array( 'NewUser' => 'nowy', 'Next' => 'Następny', 'No' => 'Nie', - 'NoDetectedCameras' => 'Nie wykryto kamer', // Added - 2009-03-31 + 'NoDetectedCameras' => 'Nie wykryto kamer', + 'NoDetectedProfiles' => 'Brak wykrytych profili', 'NoFramesRecorded' => 'Brak zapisanych ramek dla tego zdarzenia', 'NoGroup' => 'Brak grupy', - 'NoSavedFilters' => 'BrakZapisanychFiltrów', + 'NoSavedFilters' => 'Brak zapisanych filtrów', 'NoStatisticsRecorded' => 'Brak zapisanych statystyk dla tego zdarzenia/ramki', 'None' => 'Brak', 'NoneAvailable' => 'Niedostępne', 'Normal' => 'Normalny', 'Notes' => 'Uwagi', - 'NumPresets' => 'Num Presets', - 'Off' => 'Off', - 'On' => 'On', - 'OnvifCredentialsIntro'=> 'Please supply user name and password for the selected camera.
If no user has been created for the camera then the user given here will be created with the given password.

', // Added - 2015-04-18 - 'OnvifProbe' => 'ONVIF', // Added - 2015-04-18 - 'OnvifProbeIntro' => 'The list below shows detected ONVIF cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 + 'NumPresets' => 'Liczba ustawień predefiniowanych', + 'Off' => 'Wyłącz', + 'On' => 'Włącz', + 'OnvifCredentialsIntro'=> 'Podaj nazwę użytkownika i hasło dla wybranej kamery.
Jeśli nie utworzono żadnego użytkownika dla kamery, użytkownik podany tutaj zostanie utworzony z podanym hasłem.

', + 'OnvifProbe' => 'ONVIF', + 'OnvifProbeIntro' => 'Poniższa lista pokazuje wykryte kamery ONVIF i informacje, czy są one już używane lub dostępne do wyboru.

Wybierz żądany wpis z listy poniżej.

Należy pamiętać, że nie wszystkie kamery mogą zostać wykryte, a wybór kamery z poniższej listy może zastąpić wszystkie wartości skonfigurowane dla bieżącego monitora.

', 'OpEq' => 'równy', 'OpGt' => 'większe od', 'OpGtEq' => 'większe lub równe od', 'OpIn' => 'w zestawie', + 'OpIs' => 'jest', + 'OpIsNot' => 'nie jest', 'OpLt' => 'mniejsze od', 'OpLtEq' => 'mniejsze lub równe od', 'OpMatches' => 'pasujące', @@ -552,17 +582,18 @@ $SLANG = array( 'Open' => 'Otwórz', 'OptionHelp' => 'OpcjePomoc', 'OptionRestartWarning' => 'Te zmiany nie przyniosą natychmiastowego efektu\ndopóki system pracuje. Kiedy zakończysz robić zmiany\nproszę koniecznie zrestartować ZoneMinder.', + 'OptionalEncoderParam' => 'Opcjonalne parametry enkodera', 'Options' => 'Opcje', 'OrEnterNewName' => 'lub wpisz nową nazwę', 'Order' => 'Kolejność', 'Orientation' => 'Orientacja', - 'Out' => 'Out', + 'Out' => 'Wyjście', 'OverwriteExisting' => 'Nadpisz istniejące', 'Paged' => 'Stronicowane', - 'Pan' => 'Pan', - 'PanLeft' => 'Pan Left', - 'PanRight' => 'Pan Right', - 'PanTilt' => 'Pan/Tilt', + 'Pan' => 'Panoramiczny', + 'PanLeft' => 'Przesuń w lewo', + 'PanRight' => 'Przesuń w prawo', + 'PanTilt' => 'Panorama/Odchylenie', 'Parameter' => 'Parametr', 'Password' => 'Hasło', 'PasswordsDifferent' => 'Hasła: nowe i potwierdzone są różne!', @@ -570,156 +601,170 @@ $SLANG = array( 'Pause' => 'Pauza', 'Phone' => 'Telefon', 'PhoneBW' => 'Tel. prz.', - 'Pid' => 'PID', // Added - 2011-06-16 - 'PixelDiff' => 'Pixel Diff', + 'Pid' => 'PID', + 'PixelDiff' => 'Różnica pikseli', 'Pixels' => 'pikseli', 'Play' => 'Odtwórz', - 'PlayAll' => 'Play All', + 'PlayAll' => 'Odtwórz wszystkie', 'PleaseWait' => 'Proszę czekać', - 'Plugins' => 'Plugins', - 'Point' => 'Point', + 'Plugins' => 'Dodatki', + 'Point' => 'Punkt', 'PostEventImageBuffer' => 'Bufor obrazów po zdarzeniu', 'PreEventImageBuffer' => 'Bufor obrazów przed zdarzeniem', - 'PreserveAspect' => 'Preserve Aspect Ratio', - 'Preset' => 'Preset', - 'Presets' => 'Presets', + 'PreserveAspect' => 'Zachowaj proporcje', + 'Preset' => 'Predefiniowane ustawienie', + 'Presets' => 'Predefiniowane ustawienia', 'Prev' => 'Poprzedni', - 'Probe' => 'Probe', // Added - 2009-03-31 - 'ProfileProbe' => 'Stream Probe', // Added - 2015-04-18 - 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 - 'Progress' => 'Postęp', // Added - 2015-04-18 - 'Protocol' => 'Protocol', + 'Probe' => 'Wykrywanie', + 'ProfileProbe' => 'Wykrywanie strumienia', + 'ProfileProbeIntro' => 'Poniższa lista pokazuje istniejące profile strumieni wybranej kamery.

Wybierz żądany wpis z listy poniżej.

Należy pamiętać, że ZoneMinder nie może skonfigurować dodatkowych profili i że wybór tutaj kamery może zastąpić wszystkie wartości skonfigurowane dla bieżącego monitora.

', + 'Progress' => 'Postęp', + 'Protocol' => 'Protokół', + 'RTSPDescribe' => 'Użyj URL nośnika odpowiedzi RTSP', + 'RTSPTransport' => 'Protokół transportu RTSP', 'Rate' => 'Tempo', - 'Real' => 'Rzeczywiste', + 'Real' => 'Rzeczywista', + 'RecaptchaWarning' => 'Twój tajny klucz reCaptcha jest nieprawidłowy. Popraw to lub reCaptcha nie zadziała', 'Record' => 'Zapis', + 'RecordAudio' => 'Zapisuj dźwięk ze zdarzeniem', 'RefImageBlendPct' => 'Miks z obrazem odniesienia', 'Refresh' => 'Odśwież', 'Remote' => 'Zdalny', 'RemoteHostName' => 'Nazwa hostu zdalnego', - 'RemoteHostPath' => 'Scieżka hostu zdalnego ', + 'RemoteHostPath' => 'Ścieżka hostu zdalnego ', 'RemoteHostPort' => 'Port hostu zdalnego ', - 'RemoteHostSubPath' => 'Podścieżka hostu zdalnego', // Added - 2009-02-08 + 'RemoteHostSubPath' => 'Podścieżka hostu zdalnego', 'RemoteImageColours' => 'Kolory obrazu zdalnego', - 'RemoteMethod' => 'Remote Method', // Added - 2009-02-08 - 'RemoteProtocol' => 'Remote Protocol', // Added - 2009-02-08 + 'RemoteMethod' => 'Rodzaj zdalnego połączenia', + 'RemoteProtocol' => 'Zdalny protokół', 'Rename' => 'Zmień nazwę', 'Replay' => 'Odtwarzaj', 'ReplayAll' => 'Wszystko', 'ReplayGapless' => 'Wszystko i powtarzaj', 'ReplaySingle' => 'Bieżące zdarzenie', - 'Reset' => 'Reset', + 'ReportEventAudit' => 'Raport zdarzeń', + 'Reset' => 'Resetuj', 'ResetEventCounts' => 'Kasuj licznik zdarzeń', 'Restart' => 'Restart', 'Restarting' => 'Restartuję', 'RestrictedCameraIds' => 'Numery kamer', - 'RestrictedMonitors' => 'Restricted Monitors', - 'ReturnDelay' => 'Return Delay', - 'ReturnLocation' => 'Return Location', + 'RestrictedMonitors' => 'Monitory z ograniczeniami', + 'ReturnDelay' => 'Opóźnienie odpowiedzi', + 'ReturnLocation' => 'Lokalizacja powrotu', 'Rewind' => 'Przewijanie', 'RotateLeft' => 'Obróć w lewo', 'RotateRight' => 'Obróć w prawo', - 'RunLocalUpdate' => 'Proszę uruchom skrypt zmupdate.pl w celu aktualizacji', // Added - 2011-05-25 + 'RunLocalUpdate' => 'Proszę uruchom skrypt zmupdate.pl w celu aktualizacji', 'RunMode' => 'Tryb pracy', 'RunState' => 'Stan pracy', 'Running' => 'Pracuje', 'Save' => 'Zapisz', 'SaveAs' => 'Zapisz jako', 'SaveFilter' => 'Zapisz filtr', + 'SaveJPEGs' => 'Zapisz pliki JPEG', 'Scale' => 'Skala', 'Score' => 'Wynik', 'Secs' => 'Sekund', 'Sectionlength' => 'Długość sekcji', 'Select' => 'Wybierz', - 'SelectFormat' => 'Wybierz format', // Added - 2011-06-17 - 'SelectLog' => 'Wybierz log', // Added - 2011-06-17 - 'SelectMonitors' => 'Select Monitors', - 'SelfIntersecting' => 'Polygon edges must not intersect', - 'Set' => 'Set', + 'SelectFormat' => 'Wybierz format', + 'SelectLog' => 'Wybierz log', + 'SelectMonitors' => 'Wybierz monitory', + 'SelfIntersecting' => 'Krawędzie wielokątów nie mogą się przecinać', + 'Set' => 'Ustaw', 'SetNewBandwidth' => 'Ustaw nową przepustowość', - 'SetPreset' => 'Set Preset', + 'SetPreset' => 'Ustaw ust. predefiniowane', 'Settings' => 'Ustawienia', - 'ShowFilterWindow' => 'PokażOknoFiltru', + 'ShowFilterWindow' => 'Pokaż okno filtru', 'ShowTimeline' => 'Pokaż oś czasu', - 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckColour' => 'Kolor testu sygnału', + 'SignalCheckPoints' => 'Punkty kontroli sygnału', 'Size' => 'Rozmiar', - 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 - 'Sleep' => 'Sleep', + 'SkinDescription' => 'Zmień domyślną skórkę dla tego komputera', + 'Sleep' => 'Uśpij', 'SortAsc' => 'rosnąco', - 'SortBy' => 'Sortuj', + 'SortBy' => 'Sortuj po', 'SortDesc' => 'malejąco', 'Source' => 'Źródło', - 'SourceColours' => 'Source Colours', // Added - 2009-02-08 - 'SourcePath' => 'Source Path', // Added - 2009-02-08 - 'SourceType' => 'Typ Źródła', - 'Speed' => 'Speed', - 'SpeedHigh' => 'High Speed', - 'SpeedLow' => 'Low Speed', - 'SpeedMedium' => 'Medium Speed', - 'SpeedTurbo' => 'Turbo Speed', + 'SourceColours' => 'Kolor źródła', + 'SourcePath' => 'Ścieżka źródłowa', + 'SourceType' => 'Typ źródła', + 'Speed' => 'Prędkość', + 'SpeedHigh' => 'Wysoka prędkość', + 'SpeedLow' => 'Niska prędkość', + 'SpeedMedium' => 'Średnia prędkość', + 'SpeedTurbo' => 'Turbo prędkość', 'Start' => 'Start', 'State' => 'Stan', 'Stats' => 'Statystyki', 'Status' => 'Status', + 'StatusConnected' => 'Nagrywanie', + 'StatusNotRunning' => 'Nie pracuje', + 'StatusRunning' => 'Nie nagrywa', + 'StatusUnknown' => 'Nieznany', 'Step' => 'Krok', - 'StepBack' => 'Step Back', - 'StepForward' => 'Step Forward', - 'StepLarge' => 'Large Step', - 'StepMedium' => 'Medium Step', - 'StepNone' => 'No Step', - 'StepSmall' => 'Small Step', + 'StepBack' => 'Krok w tył', + 'StepForward' => 'Krok w przód', + 'StepLarge' => 'Duży krok', + 'StepMedium' => 'Średni krok', + 'StepNone' => 'Brak kroku', + 'StepSmall' => 'Mały krok', 'Stills' => 'Podgląd klatek', 'Stop' => 'Stop', 'Stopped' => 'Zatrzymany', + 'StorageArea' => 'Magazyn', + 'StorageScheme' => 'Schemat', 'Stream' => 'Odtwarzacz', - 'StreamReplayBuffer' => 'Stream Replay Image Buffer', + 'StreamReplayBuffer' => 'Bufor odtwarzania strumienia', 'Submit' => 'Zatwierdź', 'System' => 'System', - 'SystemLog' => 'Logi systemu', // Added - 2011-06-16 - 'TargetColorspace' => 'Target colorspace', // Added - 2015-04-18 - 'Tele' => 'Tele', - 'Thumbnail' => 'Thumbnail', - 'Tilt' => 'Tilt', + 'SystemLog' => 'Logi systemu', + 'TargetColorspace' => 'Przestrzeń kolorów źródła', + 'Tele' => 'Tel', + 'Thumbnail' => 'Miniaturka', + 'Tilt' => 'Odchylenie', 'Time' => 'Czas', 'TimeDelta' => 'Różnica czasu', 'TimeStamp' => 'Znak czasu', 'Timeline' => 'Oś czasu', - 'TimelineTip1' => 'Przeciągnij kursor myszki na wykresie, aby wyświetlić obraz migawki i szczegóły zdarzenia.', // Added 2013.08.15. - 'TimelineTip2' => 'Kliknij na kolorowe fragmenty wykresu, aby zobaczyć wydarzenie.', // Added 2013.08.15. - 'TimelineTip3' => 'Kliknij w tło, aby przybliżyć się do mniejszego okresu opartego wokół wykonanego kliknięcia..', // Added 2013.08.15. - 'TimelineTip4' => 'Użyj opcji poniżej, w celu nawigacji.', // Added 2013.08.15. + 'TimelineTip1' => 'Przeciągnij kursor myszki na wykresie, aby wyświetlić obraz migawki i szczegóły zdarzenia.', + 'TimelineTip2' => 'Kliknij na kolorowe fragmenty wykresu, aby zobaczyć wydarzenie.', + 'TimelineTip3' => 'Kliknij w tło, aby przybliżyć się do mniejszego okresu opartego wokół wykonanego kliknięcia..', + 'TimelineTip4' => 'Użyj opcji poniżej, w celu nawigacji.', 'Timestamp' => 'Czas', 'TimestampLabelFormat' => 'Format etykiety czasu', + 'TimestampLabelSize' => 'Rozmiar czcionki', 'TimestampLabelX' => 'Wsp. X etykiety czasu', 'TimestampLabelY' => 'Wsp. Y etykiety czasu', 'Today' => 'Dziś', 'Tools' => 'Narzędzia', - 'Total' => 'Total', // Added - 2011-06-16 + 'Total' => 'Całość', 'TotalBrScore' => 'Całkowity
wynik', - 'TrackDelay' => 'Track Delay', - 'TrackMotion' => 'Track Motion', + 'TrackDelay' => 'Śledź opóźnienia', + 'TrackMotion' => 'Śledź ruch', 'Triggers' => 'Wyzwalacze', - 'TurboPanSpeed' => 'Turbo Pan Speed', - 'TurboTiltSpeed' => 'Turbo Tilt Speed', + 'TurboPanSpeed' => 'Turbo prędkość panoramy', + 'TurboTiltSpeed' => 'Turbo prędkość odchylenia', 'Type' => 'Typ', 'Unarchive' => 'Usuń z archiwum', - 'Undefined' => 'Undefined', // Added - 2009-02-08 + 'Undefined' => 'Niezdefiniowany', 'Units' => 'Jednostki', 'Unknown' => 'Nieznany', - 'Update' => 'Update', + 'Update' => 'Aktualizuj', 'UpdateAvailable' => 'Jest dostępne uaktualnienie ZoneMinder ', 'UpdateNotNecessary' => 'Nie jest wymagane uaktualnienie', - 'Updated' => 'Updated', // Added - 2011-06-16 - 'Upload' => 'Upload', // Added - 2011-08-23 + 'Updated' => 'Zaktualizowane', + 'Upload' => 'Wysyłanie', 'UseFilter' => 'Użyj filtru', 'UseFilterExprsPost' => ' wyrażenie filtru', // This is used at the end of the phrase 'use N filter expressions' 'UseFilterExprsPre' => 'Użyj ', // This is used at the beginning of the phrase 'use N filter expressions' - 'UsedPlugins' => 'Used Plugins', + 'UsedPlugins' => 'Użyte dodatki', 'User' => 'Użytkownik', 'Username' => 'Nazwa użytkownika', 'Users' => 'Użytkownicy', - 'V4L' => 'V4L', // Added - 2015-04-18 - 'V4LCapturesPerFrame' => 'Captures Per Frame', // Added - 2015-04-18 - 'V4LMultiBuffer' => 'Multi Buffering', // Added - 2015-04-18 + 'V4L' => 'V4L', + 'V4LCapturesPerFrame' => 'Przechwycenia na ramkę', + 'V4LMultiBuffer' => 'Multi buforowanie', 'Value' => 'Wartość', 'Version' => 'Wersja', 'VersionIgnore' => 'Zignoruj tą wersję', @@ -727,27 +772,29 @@ $SLANG = array( 'VersionRemindHour' => 'Przypomnij po 1 godzinie', 'VersionRemindNever' => 'Nie przypominaj o nowych wersjach', 'VersionRemindWeek' => 'Przypomnij po 1 tygodniu', - 'Video' => 'Eksport Video', + 'Video' => 'Eksport wideo', 'VideoFormat' => 'Format nagrania', - 'VideoGenFailed' => 'Generowanie filmu Video nie powiodło się!', + 'VideoGenFailed' => 'Generowanie filmu wideo nie powiodło się!', 'VideoGenFiles' => 'Lista wygenerowanych plików:', - 'VideoGenNoFiles' => 'Nie odnaleziono plików Video', - 'VideoGenParms' => 'Parametery generowania filmu Video', + 'VideoGenNoFiles' => 'Nie odnaleziono plików wideo', + 'VideoGenParms' => 'Parametry generowania filmu wideo', 'VideoGenSucceeded' => 'Wygenerowano pomyślnie!', - 'VideoSize' => 'Rozmiar filmu Video', + 'VideoSize' => 'Rozmiar filmu wideo', + 'VideoWriter' => 'Sposób zapisu wideo', 'View' => 'Podgląd', 'ViewAll' => 'Pokaż wszystko', 'ViewEvent' => 'Pokaż zdarzenie', 'ViewPaged' => 'Pokaż stronami', - 'Wake' => 'Wake', + 'Wake' => 'Obudź', 'WarmupFrames' => 'Ignorowane ramki', 'Watch' => 'podgląd', 'Web' => 'Sieć', - 'WebColour' => 'Web Colour', + 'WebColour' => 'Kolor strony', + 'WebSiteUrl' => 'URL strony', 'Week' => 'Tydzień', 'White' => 'Biel', 'WhiteBalance' => 'Balans bieli', - 'Wide' => 'Wide', + 'Wide' => 'Szerokość', 'X' => 'X', 'X10' => 'X10', 'X10ActivationString' => 'X10: łańcuch aktywujący', @@ -758,20 +805,20 @@ $SLANG = array( 'YouNoPerms' => 'Nie masz uprawnień na dostęp do tego zasobu.', 'Zone' => 'Strefa', 'ZoneAlarmColour' => 'Kolor alarmu (Red/Green/Blue)', - 'ZoneArea' => 'Zone Area', - 'ZoneExtendAlarmFrames' => 'Extend Alarm Frame Count', - 'ZoneFilterSize' => 'Filter Width/Height (pixels)', - 'ZoneMinMaxAlarmArea' => 'Min/Max Alarmed Area', - 'ZoneMinMaxBlobArea' => 'Min/Max Blob Area', - 'ZoneMinMaxBlobs' => 'Min/Max Blobs', - 'ZoneMinMaxFiltArea' => 'Min/Max Filtered Area', - 'ZoneMinMaxPixelThres' => 'Min/Max Pixel Threshold (0-255)', - 'ZoneMinderLog' => 'ZoneMinder Log', // Added - 2011-06-17 - 'ZoneOverloadFrames' => 'Overload Frame Ignore Count', + 'ZoneArea' => 'Obszar strefy', + 'ZoneExtendAlarmFrames' => 'Rozszerz licznik ramek alarmowych', + 'ZoneFilterSize' => 'Szerokość/wysokość filtra (piksele)', + 'ZoneMinMaxAlarmArea' => 'Min/Max obszar alarmu', + 'ZoneMinMaxBlobArea' => 'Min/Max obszar plamki', + 'ZoneMinMaxBlobs' => 'Min/Max plamki', + 'ZoneMinMaxFiltArea' => 'Min/Max obszar filtrowany', + 'ZoneMinMaxPixelThres' => 'Min/Max próg pikseli (0-255)', + 'ZoneMinderLog' => 'Log ZoneMinder', + 'ZoneOverloadFrames' => 'Liczba ignorowanych ramek po przeciążeniu alarmu', 'Zones' => 'Strefy', - 'Zoom' => 'Zoom', - 'ZoomIn' => 'Zoom In', - 'ZoomOut' => 'Zoom Out', + 'Zoom' => 'Powiększenie', + 'ZoomIn' => 'Przybliż', + 'ZoomOut' => 'Oddal', ); // Complex replacements with formatting and/or placements, must be passed through sprintf @@ -783,7 +830,7 @@ $CLANG = array( 'MonitorCount' => '%1$s %2$s', 'MonitorFunction' => 'Monitor %1$s Funkcja', 'RunningRecentVer' => 'Uruchomiłeś najnowszą wersję ZoneMinder, v%s.', - 'VersionMismatch' => 'Version mismatch, system is version %1$s, database is %2$s.', // Added - 2011-05-25 + 'VersionMismatch' => 'Niezgodność wersji, wersja systemu %1$s, bazy danych %2$s.', ); // The next section allows you to describe a series of word ending and counts used to @@ -829,7 +876,7 @@ $VLANG = array( // in to generate the correct noun form. // This is an version that could be used in the Polish language -// +// function zmVlang( $langVarArray, $count ) { $secondlastdigit = substr( $count, -2, 1 ); @@ -869,7 +916,7 @@ function zmVlang( $langVarArray, $count ) die( 'BŁĄD! zmVlang nie może skorelowac łańcucha!' ); } -// This is an example of how the function is used in the code which you can uncomment and +// This is an example of how the function is used in the code which you can uncomment and // use to test your custom function. // $monitors = 12; // Choose any number // echo $monitors." "; @@ -880,19 +927,19 @@ function zmVlang( $langVarArray, $count ) // So for example, to override the help text for ZM_LANG_DEFAULT do $OLANG = array( 'OPTIONS_FFMPEG' => array( - 'Help' => "Parameters in this field are passed on to FFmpeg. Multiple parameters can be separated by ,~~ ". - "Examples (do not enter quotes)~~~~". - "\"allowed_media_types=video\" Set datatype to request fromcam (audio, video, data)~~~~". - "\"reorder_queue_size=nnn\" Set number of packets to buffer for handling of reordered packets~~~~". - "\"loglevel=debug\" Set verbosity of FFmpeg (quiet, panic, fatal, error, warning, info, verbose, debug)" + 'Help' => "Parametry z tego pola są przekazywane do FFmpeg. Wiele parametrów może być rozdzielone przez ,~~ ". + "Przykłady (nie wpisuj cytatów)~~~~". + "\"allowed_media_types=video\" Ustaw typ danych na żądanie z kamery (audio, video, data)~~~~". + "\"reorder_queue_size=nnn\" Ustaw liczbę pakietów do buforowania do obsługi zmienionych pakietów~~~~". + "\"loglevel=debug\" Ustaw gadatliwość FFmpeg (quiet, panic, fatal, error, warning, info, verbose, debug)" ), 'OPTIONS_LIBVLC' => array( - 'Help' => "Parameters in this field are passed on to libVLC. Multiple parameters can be separated by ,~~ ". - "Examples (do not enter quotes)~~~~". - "\"--rtp-client-port=nnn\" Set local port to use for rtp data~~~~". - "\"--verbose=2\" Set verbosity of libVLC" + 'Help' => "Parametry w tym polu są przekazywane do libVLC. Wiele parametrów może być rozdzielone przez ,~~ ". + "Przykłady (nie wpisuj cytatów)~~~~". + "\"--rtp-client-port=nnn\" Ustaw port lokalny, który ma być używany dla danych rtp~~~~". + "\"--verbose=2\" Ustaw gadatliwość libVLC" ), - + // 'LANG_DEFAULT' => array( // 'Prompt' => "This is a new prompt for this option", // 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked" diff --git a/web/lang/pt_br.php b/web/lang/pt_br.php index c7c3caf42..80ded9e3d 100644 --- a/web/lang/pt_br.php +++ b/web/lang/pt_br.php @@ -18,6 +18,8 @@ $SLANG = array( 'Actual' => 'Atual', 'AddNewControl' => 'Add New Control', 'AddNewMonitor' => 'Adicionar Monitor', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Adicionar Usuário', 'AddNewZone' => 'Adicionar Zona', 'Alarm' => 'Alarme', @@ -45,22 +47,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Status/Arquivamento', 'AttrAvgScore' => 'Maior Score', 'AttrCause' => 'Cause', - 'AttrDate' => 'Data', - 'AttrDateTime' => 'Data/Horario', 'AttrDiskBlocks' => 'Blocos de Disco', 'AttrDiskPercent' => 'Porcentagem de Disco', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Duração', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Imagens', 'AttrId' => 'Id', 'AttrMaxScore' => 'Max. Score', 'AttrMonitorId' => 'Id do Monitor', 'AttrMonitorName' => 'Nome do Monitor', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Nome', 'AttrNotes' => 'Notes', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'System Load', - 'AttrTime' => 'Horário', 'AttrTotalScore' => 'Score Total', - 'AttrWeekday' => 'Dia/Semana', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Auto Stop Timeout', 'Available' => 'Available', // Added - 2009-03-31 @@ -93,9 +105,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer', 'BadSectionLength' => 'Section length must be an integer of 30 or more', 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Stream replay buffer must be an integer of zero or more', 'BadWarmupCount' => 'Warmup frames must be an integer of zero or more', 'BadWebColour' => 'Web colour must be a valid web colour string', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Width must be set to a valid value', 'Bandwidth' => 'Larg/Banda', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -131,6 +145,7 @@ $SLANG = array( 'CanMoveRel' => 'Can Move Relative', 'CanPan' => 'Can Pan' , 'CanReset' => 'Can Reset', + 'CanReboot' => 'Can Reboot', 'CanSetPresets' => 'Can Set Presets', 'CanSleep' => 'Can Sleep', 'CanTilt' => 'Can Tilt', @@ -159,10 +174,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Choose Preset', 'Clear' => 'Clear', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Fechar', 'Colour' => 'Cor', 'Command' => 'Command', 'Component' => 'Component', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Config', 'ConfiguredFor' => 'Configurado para', 'ConfirmDeleteEvents' => 'Are you sure you wish to delete the selected events?', @@ -212,7 +229,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'Please Donate', 'DonateAlready' => 'No, I\'ve already donated', - 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', + 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', 'DonateRemindDay' => 'Not yet, remind again in 1 day', 'DonateRemindHour' => 'Not yet, remind again in 1 hour', 'DonateRemindMonth' => 'Not yet, remind again in 1 month', @@ -220,9 +237,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Not yet, remind again in 1 week', 'DonateYes' => 'Yes, I\'d like to donate now', 'Download' => 'Download', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 'Duration' => 'Duração', 'Edit' => 'Editar', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Enable Alarms', 'Enabled' => 'Habilitado', @@ -239,6 +258,7 @@ $SLANG = array( 'Events' => 'Eventos', 'Exclude' => 'Excluir', 'Execute' => 'Execute', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Export', 'ExportDetails' => 'Export Event Details', 'ExportFailed' => 'Export Failed', @@ -268,8 +288,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Executar comando p/ resultados', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Enviar Mensagem dos resultados', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Px de Filtro', 'FilterUnset' => 'You must specify a filter width and height', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Fazer upload dos resultados', 'FilterVideoEvents' => 'Create video for all matches', 'Filters' => 'Filters', @@ -294,6 +316,7 @@ $SLANG = array( 'Function' => 'Função', 'Gain' => 'Gain', 'General' => 'General', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Gerar Video', 'GeneratingVideo' => 'Gerando Video', 'GoToZoneMinder' => 'Ir Para ZoneMinder.com', @@ -314,6 +337,7 @@ $SLANG = array( 'High' => 'Alto', 'HighBW' => 'Alta L/B', 'Home' => 'Home', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Hora', 'Hue' => 'Saturação', 'Id' => 'Id', @@ -338,6 +362,7 @@ $SLANG = array( 'Line' => 'Line', // Added - 2011-06-16 'LinkedMonitors' => 'Linked Monitors', 'List' => 'List', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Carga', 'Local' => 'Local', 'Log' => 'Log', // Added - 2011-06-16 @@ -424,6 +449,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 'Monitors' => 'Monitores', 'Montage' => 'Montagem', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Mês', 'More' => 'More', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -450,6 +476,7 @@ $SLANG = array( 'Next' => 'Próx', 'No' => 'Não', 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Não há imagens gravadas neste evento', 'NoGroup' => 'No Group', 'NoSavedFilters' => 'SemFiltrosSalvos', @@ -468,6 +495,8 @@ $SLANG = array( 'OpGt' => 'maior que', 'OpGtEq' => 'maior que ou igual a', 'OpIn' => 'no set', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'menor que', 'OpLtEq' => 'menor que ou igual a', 'OpMatches' => 'combina', @@ -477,6 +506,7 @@ $SLANG = array( 'Open' => 'Open', 'OptionHelp' => 'OpçãoAjuda', 'OptionRestartWarning' => 'Reinicie o Zoneminder para que as mudanças tenham efeito', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Opções', 'OrEnterNewName' => 'ou defina novo nome', 'Order' => 'Order', @@ -514,9 +544,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Vel.', 'Real' => 'Real', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Gravar', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Referência de imagem Blend %ge', 'Refresh' => 'Atualizar', 'Remote' => 'Remoto', @@ -532,6 +566,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Reset', 'ResetEventCounts' => 'Resetar contagem de eventos', 'Restart' => 'Reiniciar', @@ -550,6 +585,7 @@ $SLANG = array( 'Save' => 'Salvar', 'SaveAs' => 'Salvar Como', 'SaveFilter' => 'Salvar Filtro', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Tamanho', 'Score' => 'Score', 'Secs' => 'Segs', @@ -566,6 +602,7 @@ $SLANG = array( 'ShowFilterWindow' => 'MostrarJanelaDeFiltros', 'ShowTimeline' => 'Show Timeline', 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Size', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 'Sleep' => 'Sleep', @@ -585,6 +622,10 @@ $SLANG = array( 'State' => 'Estado', 'Stats' => 'Status', 'Status' => 'Status', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Step', 'StepBack' => 'Step Back', 'StepForward' => 'Step Forward', @@ -595,6 +636,8 @@ $SLANG = array( 'Stills' => 'Imagens', 'Stop' => 'Parar', 'Stopped' => 'Parado', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Contínuo', 'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Submit' => 'Submit', @@ -614,6 +657,7 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => 'Tempo', 'TimestampLabelFormat' => 'Formato de etiqueta de tempo', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'posição de etiqueta X', 'TimestampLabelY' => 'posição de etiqueta Y', 'Today' => 'Today', @@ -660,6 +704,7 @@ $SLANG = array( 'VideoGenParms' => 'Parametros de geração de vídeo', 'VideoGenSucceeded' => 'Video Generation Succeeded!', 'VideoSize' => 'Tamanho do vídeo', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Ver', 'ViewAll' => 'Ver Tudo', 'ViewEvent' => 'View Event', @@ -669,6 +714,7 @@ $SLANG = array( 'Watch' => 'Assistir', 'Web' => 'Web', 'WebColour' => 'Web Colour', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Semana', 'White' => 'White', 'WhiteBalance' => 'White Balance', diff --git a/web/lang/ro_ro.php b/web/lang/ro_ro.php index a36c4de6c..20b2bf647 100644 --- a/web/lang/ro_ro.php +++ b/web/lang/ro_ro.php @@ -49,6 +49,8 @@ $SLANG = array( 'Actual' => 'Real', 'AddNewControl' => 'Adaugă control nou', 'AddNewMonitor' => 'Adaugă monitor', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Adaugă utilizator', 'AddNewZone' => 'Adaugă zonă', 'Alarm' => 'Alarma', @@ -76,22 +78,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Stare arhiva', 'AttrAvgScore' => 'Cota medie', 'AttrCause' => 'Cauza', - 'AttrDate' => 'Data', - 'AttrDateTime' => 'Data/Timp', 'AttrDiskBlocks' => 'Disk Blocks', 'AttrDiskPercent' => 'Procentaj disc', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Durata', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Cadre', 'AttrId' => 'Nr.', 'AttrMaxScore' => 'Cota max', 'AttrMonitorId' => 'Monitor nr.', 'AttrMonitorName' => 'Nume monitor', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Nume', 'AttrNotes' => 'Notes', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'System Load', - 'AttrTime' => 'Time', 'AttrTotalScore' => 'Cota total', - 'AttrWeekday' => 'Zi săpt.', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Auto Stop Timeout', 'Available' => 'Available', // Added - 2009-03-31 @@ -124,9 +136,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer', 'BadSectionLength' => 'Section length must be an integer of 30 or more', 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Stream replay buffer must be an integer of zero or more', 'BadWarmupCount' => 'Warmup frames must be an integer of zero or more', 'BadWebColour' => 'Web colour must be a valid web colour string', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Width must be set to a valid value', 'Bandwidth' => 'Laţime de bandă', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -162,6 +176,7 @@ $SLANG = array( 'CanMoveRel' => 'Mişcare relativă', 'CanPan' => 'Rotativ' , 'CanReset' => 'Can Reset', + 'CanReboot' => 'Can Reboot', 'CanSetPresets' => 'Can Set Presets', 'CanSleep' => 'Can Sleep', 'CanTilt' => 'Se poate înclina', @@ -190,10 +205,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Choose Preset', 'Clear' => 'Clear', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Închide', 'Colour' => 'Culoare', 'Command' => 'Comanda', 'Component' => 'Component', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Config', 'ConfiguredFor' => 'Configurat pentru', 'ConfirmDeleteEvents' => 'Are you sure you wish to delete the selected events?', @@ -243,7 +260,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'Please Donate', 'DonateAlready' => 'No, I\'ve already donated', - 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', + 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', 'DonateRemindDay' => 'Not yet, remind again in 1 day', 'DonateRemindHour' => 'Not yet, remind again in 1 hour', 'DonateRemindMonth' => 'Not yet, remind again in 1 month', @@ -251,9 +268,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Not yet, remind again in 1 week', 'DonateYes' => 'Yes, I\'d like to donate now', 'Download' => 'Download', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 'Duration' => 'Durata', 'Edit' => 'Modific', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Enable Alarms', 'Enabled' => 'Activ', @@ -270,6 +289,7 @@ $SLANG = array( 'Events' => 'Evenim.', 'Exclude' => 'Exclude', 'Execute' => 'Execute', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Export', 'ExportDetails' => 'Export Event Details', 'ExportFailed' => 'Export Failed', @@ -299,8 +319,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Execută comanda pentru toate rezultatele', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Trimite mesaj pentru toate rezultatele', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filter Px', 'FilterUnset' => 'You must specify a filter width and height', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Încarcă toate rezultatele', 'FilterVideoEvents' => 'Create video for all matches', 'Filters' => 'Filters', @@ -325,6 +347,7 @@ $SLANG = array( 'Function' => 'Funcţie', 'Gain' => 'Gain', 'General' => 'General', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Generează video', 'GeneratingVideo' => 'Generez video', 'GoToZoneMinder' => 'Du-te la ZoneMinder.com', @@ -345,6 +368,7 @@ $SLANG = array( 'High' => 'Mare', 'HighBW' => 'B/W mare', 'Home' => 'Home', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Ora', 'Hue' => 'Nuanţă', 'Id' => 'Nr.', @@ -369,6 +393,7 @@ $SLANG = array( 'Line' => 'Line', // Added - 2011-06-16 'LinkedMonitors' => 'Linked Monitors', 'List' => 'List', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Load', 'Local' => 'Local', 'Log' => 'Log', // Added - 2011-06-16 @@ -455,6 +480,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 'Monitors' => 'Monitoare', 'Montage' => 'Montage', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Luna', 'More' => 'More', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -481,6 +507,7 @@ $SLANG = array( 'Next' => 'Urmator', 'No' => 'Nu', 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Nu exista cadre inregistrate pentru acest eveniment.', 'NoGroup' => 'No Group', 'NoSavedFilters' => 'LipsaFiltruSalvat', @@ -499,6 +526,8 @@ $SLANG = array( 'OpGt' => 'mai mare ca', 'OpGtEq' => 'mai mare sau egal cu', 'OpIn' => 'in set', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'mai mic decât', 'OpLtEq' => 'mai mic sau egal cu', 'OpMatches' => 'matches', @@ -508,6 +537,7 @@ $SLANG = array( 'Open' => 'Deschide', 'OptionHelp' => 'OptionHelp', 'OptionRestartWarning' => 'Aceste schimbari nu se aplica in timpul rularii.\n Dupa ce ati terminat setarile va rugam reporniti ZoneMinder.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Opţiuni', 'OrEnterNewName' => 'sau denumire nouă', 'Order' => 'Order', @@ -545,9 +575,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Rate', 'Real' => 'Real', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Înregistrare', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Combinare imagine referinta(%)', 'Refresh' => 'Actualizează', 'Remote' => 'Remote', @@ -563,6 +597,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Reset', 'ResetEventCounts' => 'Reset Event Counts', 'Restart' => 'Reporneşte', @@ -581,6 +616,7 @@ $SLANG = array( 'Save' => 'Salvez', 'SaveAs' => 'Salvează ca', 'SaveFilter' => 'Salvează filtru', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Scara', 'Score' => 'Cota', 'Secs' => 'Sec', @@ -597,6 +633,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Fereastra filtre', 'ShowTimeline' => 'Show Timeline', 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Size', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 'Sleep' => 'Sleep', @@ -616,6 +653,10 @@ $SLANG = array( 'State' => 'Stare', 'Stats' => 'Statistici', 'Status' => 'Stare', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Step', 'StepBack' => 'Step Back', 'StepForward' => 'Step Forward', @@ -626,6 +667,8 @@ $SLANG = array( 'Stills' => 'Statice', 'Stop' => 'Opreşte', 'Stopped' => 'Oprit', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Flux', 'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Submit' => 'Trimite', @@ -645,6 +688,7 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => 'Format timp', 'TimestampLabelFormat' => 'Format eticheta format timp', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'Format timp eticheta X', 'TimestampLabelY' => 'Format timp eticheta Y', 'Today' => 'Azi', @@ -691,6 +735,7 @@ $SLANG = array( 'VideoGenParms' => 'Parametrii generare video', 'VideoGenSucceeded' => 'Video Generation Succeeded!', 'VideoSize' => 'Mărime video', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Vizual', 'ViewAll' => 'Vizual. tot', 'ViewEvent' => 'View Event', @@ -700,6 +745,7 @@ $SLANG = array( 'Watch' => 'Watch', 'Web' => 'Web', 'WebColour' => 'Web Colour', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Săpt.', 'White' => 'Alb', 'WhiteBalance' => 'Balans alb', @@ -837,10 +883,6 @@ $OLANG = array( 'Prompt' => "Directorul în care sunt stocate evenimentele", 'Help' => "Acesta este subdirectorul în care sunt salvate imaginile generate de evenimente şi alte fişiere. Implicit este un subdirector al directorului rădăcina zoneminder; dacă spaţiul nu vă permite puteţi să stocaţi imaginile pe altă partiţie, caz în care ar trebui să faceţi un link la subdirectorul implicit." ), - 'DIR_IMAGES' => array( - 'Prompt' => "Directorul în care sunt stocate imaginile", - 'Help' => "ZoneMinder generează multe imagini, majoritate asociate cu evenimente. În acest director vor fi stocate imaginile neasociate evenimentelor." - ), 'DIR_SOUNDS' => array( 'Prompt' => "Directorul cu sunetele care pot fi folosite de ZoneMinder", 'Help' => "ZoneMinder poate rula un sunet atunci când este detectată o alarmă. Acesta este directorul în care este stocat sunetul care va fi rulat." diff --git a/web/lang/ru_ru.php b/web/lang/ru_ru.php index 8d0d24a7d..6664e4d97 100644 --- a/web/lang/ru_ru.php +++ b/web/lang/ru_ru.php @@ -20,6 +20,7 @@ // ZoneMinder Russian Translation by Borodin A.S. // ZoneMinder Russian Translation updated by IDDQDesnik, 2017 +// ZoneMinder Russian Translation updated by santos995, 2019 // Notes for Translators // 0. Get some credit, put your name in the line above (optional) @@ -76,8 +77,11 @@ $SLANG = array( '32BitColour' => '32 битный цвет', // Added - 2011-06-15 '8BitGrey' => '256 оттенков серого', 'Action' => 'Действие', + 'Actual' => 'Актуальный', // Edited - 2019-03-25 'AddNewControl' => 'Добавить новый', 'AddNewMonitor' => 'Добавить монитор', + 'AddNewServer' => 'Добавить новый сервер', // Edited - 2019-03-25 + 'AddNewStorage' => 'Добавить новое хранилище', // Edited - 2019-03-25 'AddNewUser' => 'Добавить пользователя', 'AddNewZone' => 'Добавить зону', 'Alarm' => 'Тревога', @@ -89,7 +93,6 @@ $SLANG = array( 'AlarmPx' => 'Пкс трев.', 'AlarmRGBUnset' => 'You must set an alarm RGB colour', 'AlarmRefImageBlendPct'=> 'Смешение опорного кадра тревоги, %', // Added - 2015-04-18 - 'AlmRefImageBlendPct' => 'Смешение опорного кадра тревоги, %', 'Alert' => 'Настороже', 'All' => 'Все', 'AnalysisFPS' => 'Частота анализа (к/с)', // Added - 2015-07-22 @@ -106,24 +109,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Статус архивации', 'AttrAvgScore' => 'Сред. оценка', 'AttrCause' => 'Причина', - 'AttrDate' => 'Дата', - 'AttrDateTime' => 'Дата/Время', 'AttrDiskBlocks' => 'Диск, блоки', 'AttrDiskPercent' => 'Диск, проценты', + 'AttrDiskSpace' => 'Дисковое пространство', // Edited - 2019-03-24 'AttrDuration' => 'Длительность', + 'AttrEndDate' => 'Дата окончания', // Edited - 2019-03-24 + 'AttrEndDateTime' => 'Дата/время окончания', // Edited - 2019-03-24 + 'AttrEndTime' => 'Время окончания', // Edited - 2019-03-24 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Фильтр для серверов запущен', // Edited - 2019-03-24 'AttrFrames' => 'Кол-во кадров', 'AttrId' => 'ИД', 'AttrMaxScore' => 'Макс. оценка', 'AttrMonitorId' => 'ИД Монитора', 'AttrMonitorName' => 'Название Монитора', + 'AttrMonitorServer' => 'Монитор серверов запущен', // Edited - 2019-03-24 'AttrName' => 'Имя', 'AttrNotes' => 'Примечание', - 'AttrServerId' => 'ИД сервера', - 'AttrServerName' => 'Имя сервера', + 'AttrStartDate' => 'Дата начала', // Edited - 2019-03-24 + 'AttrStartDateTime' => 'Дата/Время начала', // Edited - 2019-03-24 + 'AttrStartTime' => 'Время начала', // Edited - 2019-03-24 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'Нагрузка проц.', - 'AttrTime' => 'Время', 'AttrTotalScore' => 'Сумм. оценка', - 'AttrWeekday' => 'День недели', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Тайм-аут автоостановки', 'Available' => 'Доступно', // Added - 2009-03-31 @@ -156,9 +167,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Смешение опорного кадра должно быть положительным и целочисленным', 'BadSectionLength' => 'Длина секции должна быть целочисленной и большей либо равной тридцати', 'BadSignalCheckColour' => 'Цвет проверки сигнала должен быть правильной строкой формата RGB', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Буфер потока повторного воспроизведения должен быть целочисленным и большим либо равным нулю', 'BadWarmupCount' => 'Кол-во кадров разогрева должно быть целочисленным и большим либо равным нулю', 'BadWebColour' => 'Цвет отметки должен быть правильным Web-цветом', + 'BadWebSitePath' => 'Пожалуйста, введите полной название сайта url, включая http:// или https:// префикс.', // Edited - 2019-03-24 'BadWidth' => 'Неправильная ширина', 'Bandwidth' => 'канал', 'BandwidthHead' => 'канал', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing; @@ -166,9 +179,9 @@ $SLANG = array( 'BlobSizes' => 'Размер объектов', 'Blobs' => 'Кол-во объектов', 'Brightness' => 'Яркость', - 'Buffer' => 'Buffer', // Added - 2015-04-18 + 'Buffer' => 'Буфер', // Edited - 2019-03-24 'Buffers' => 'Буферы', - 'CSSDescription' => 'Change the default css for this computer', // Added - 2015-04-18 + 'CSSDescription' => 'Изменить стандартный CSS для данного компьютера', // Edited - 2019-03-24 'CanAutoFocus' => 'Автофокус', 'CanAutoGain' => 'Автоусиление', 'CanAutoIris' => 'Автодиафрагма', @@ -194,6 +207,7 @@ $SLANG = array( 'CanMoveRel' => 'Относительное перемещение', 'CanPan' => 'Панорама' , 'CanReset' => 'Сброс', + 'CanReboot' => 'Перезагрузка', // Added - 2019-03-24 'CanSetPresets' => 'Создание предустановок', 'CanSleep' => 'Сон', 'CanTilt' => 'Наклон', @@ -212,20 +226,22 @@ $SLANG = array( 'CaptureHeight' => 'Размер по Y', 'CaptureMethod' => 'Метод захвата', // Added - 2009-02-08 'CapturePalette' => 'Режим захвата', - 'CaptureResolution' => 'Capture Resolution', // Added - 2015-04-18 + 'CaptureResolution' => 'Разрешение', // Edited - 2019-03-24 'CaptureWidth' => 'Размер по X', 'Cause' => 'Причина', 'CheckMethod' => 'Метод проверки тревоги', 'ChooseDetectedCamera' => 'Выберите камеру', // Added - 2009-03-31 'ChooseFilter' => 'Выбрать фильтр', - 'ChooseLogFormat' => 'Choose a log format', // Added - 2011-06-17 + 'ChooseLogFormat' => 'Выбрать формат лога', // Edited - 2019-03-25 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Выберите предустановку', 'Clear' => 'Очистить', // Added - 2011-06-16 + 'CloneMonitor' => 'Клонировать', // Edited - 2019-03-25 'Close' => 'Закрыть', 'Colour' => 'Цвет', 'Command' => 'Command', 'Component' => 'Компонент', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Config', 'ConfiguredFor' => 'настроен на', 'ConfirmDeleteEvents' => 'Вы действительно хотите удалить выбранные события?', @@ -234,7 +250,7 @@ $SLANG = array( 'ConjOr' => 'или', 'Console' => 'Сервер', 'ContactAdmin' => 'Пожалуйста обратитесь к вашему администратору.', - 'Continue' => 'Continue', + 'Continue' => 'Продолжить', // Added - 2019-03-25 'Contrast' => 'Контраст', 'Control' => 'Управление', 'ControlAddress' => 'Адрес устройства', @@ -248,12 +264,12 @@ $SLANG = array( 'CycleWatch' => 'Циклический просмотр', 'DateTime' => 'Дата/Время', // Added - 2011-06-16 'Day' => 'День', - 'Debug' => 'Debug', + 'Debug' => 'Отладка', // Added - 2019-03-25 'DefaultRate' => 'Скорость по умолчанию', 'DefaultScale' => 'Масштаб по умолчанию', 'DefaultView' => 'Вид по умолчанию', 'Deinterlacing' => 'Устранение чересстрочности', // Added - 2015-04-18 - 'Delay' => 'Delay', // Added - 2015-04-18 + 'Delay' => 'Задержка', // Edited - 2019-03-25 'Delete' => 'Удалить', 'DeleteAndNext' => 'Удалить & след.', 'DeleteAndPrev' => 'Удалить & пред.', @@ -261,18 +277,18 @@ $SLANG = array( 'Description' => 'Описание', 'DetectedCameras' => 'Найденные камеры', // Added - 2009-03-31 'DetectedProfiles' => 'Найденные профили', // Added - 2015-04-18 - 'Device' => 'Device', // Added - 2009-02-08 + 'Device' => 'Устройство', // Edited - 2019-03-25 'DeviceChannel' => 'Канал', 'DeviceFormat' => 'Формат', 'DeviceNumber' => 'Номер устройства', 'DevicePath' => 'Путь к устройству', - 'Devices' => 'Devices', + 'Devices' => 'Устройства', // Edited - 2019-03-25 'Dimensions' => 'Размеры', 'DisableAlarms' => 'Запретить тревогу', 'Disk' => 'Диск', 'Display' => 'Display', // Added - 2011-01-30 'Displaying' => 'Отображено', // Added - 2011-06-16 - 'DoNativeMotionDetection'=> 'Do Native Motion Detection', + 'DoNativeMotionDetection'=> 'Использовать встроенное обнаружение движения', // Edited - 2019-03-25 'Donate' => 'Поддержите проект', 'DonateAlready' => 'Нет, я уже сделал пожертвование', 'DonateEnticement' => 'Вы какое-то время используете ZoneMinder и, надеемся, находите его полезным дополнением к вашей домашней или рабочей безопасности. Хотя ZoneMinder есть и будет оставаться свободным и бесплатным, он требует денег на разработку и поддержку. Если Вы хотите поддержать его будущее развитие и новые функции, пожалуйста сделайте пожертвование. Это, конечно, необязательно, но очень высоко ценится. Вы можете пожертвовать любую сумму.

Если Вы хотите сделать пожертвование, то выберите соответствующий вариант ниже или перейдите по адресу https://www.bountysource.com/teams/zoneminder в вашем браузере.

Спасибо за использование ZoneMinder, и не забывайте посетить форум на ZoneMinder.com для поддержки и пожеланий как сделать ZoneMinder еще лучше.', @@ -283,9 +299,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Нет, не сейчас, напомнить через неделю', 'DonateYes' => 'Да, я хотел бы сделать пожертвование', 'Download' => 'Скачать', - 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 + 'DownloadVideo' => 'Скачать видео', // Added - 2019-03-24 + 'DuplicateMonitorName' => 'Скопировать имя монитора', // Added - 2019-03-25 'Duration' => 'Длительность', 'Edit' => 'Редактирование', + 'EditLayout' => 'Редактирование шаблона', // Added - 2019-03-25 'Email' => 'Email', 'EnableAlarms' => 'Разрешить тревогу', 'Enabled' => 'Включен', @@ -296,12 +314,13 @@ $SLANG = array( 'Etc' => 'и т.д.', 'Event' => 'Событие', 'EventFilter' => 'Фильтр событий', - 'EventId' => 'Event Id', - 'EventName' => 'Event Name', + 'EventId' => 'Id события', // Added - 2019-03-25 + 'EventName' => 'Имя события', // Added - 2019-03-25 'EventPrefix' => 'Префикс события', 'Events' => 'События', 'Exclude' => 'Исключить', 'Execute' => 'Выполнить', + 'Exif' => 'Включить EXIF информацию в изображение', // Added - 2019-03-24 'Export' => 'Экспорт', 'ExportDetails' => 'Экспортировать описание события', 'ExportFailed' => 'Ошибка экспорта', @@ -331,14 +350,16 @@ $SLANG = array( 'FilterExecuteEvents' => 'Выполнить команду над выбранным', 'FilterLog' => 'Фильтр лога', // Added - 2015-04-18 'FilterMessageEvents' => 'Message details of all matches', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Пкс фильтра', - 'FilterUnset' => 'You must specify a filter width and height', - 'FilterUploadEvents' => 'Upload all matches', - 'FilterVideoEvents' => 'Create video for all matches', + 'FilterUnset' => 'Вы должны указать ширину и высоту фильтра', // Added - 2019-03-25 + 'FilterUpdateDiskSpace'=> 'Обновить используемое дисковое пространство', // Edited - 2019-03-25 + 'FilterUploadEvents' => 'Загрузить все совпадения', // Added - 2019-03-25 + 'FilterVideoEvents' => 'Создать видео для всех совпадений', // Added - 2019-03-25 'Filters' => 'Фильтры', 'First' => 'Первый', - 'FlippedHori' => 'Flipped Horizontally', - 'FlippedVert' => 'Flipped Vertically', + 'FlippedHori' => 'Перевернутый горизонтально', // Added - 2019-03-25 + 'FlippedVert' => 'Перевернутый вертикально', // Added - 2019-03-25 'FnMocord' => 'Mocord', // Added 2013.08.16. 'FnModect' => 'Modect', // Added 2013.08.16. 'FnMonitor' => 'Monitor', // Added 2013.08.16. @@ -357,6 +378,7 @@ $SLANG = array( 'Function' => 'Функция', 'Gain' => 'Gain', 'General' => 'Основные', + 'GenerateDownload' => 'Сгенерировать загрузку', // Edited - 2019-03-25 'GenerateVideo' => 'Генерировать видео', 'GeneratingVideo' => 'Генерируется видео', 'GoToZoneMinder' => 'Перейти на ZoneMinder.com', @@ -377,6 +399,7 @@ $SLANG = array( 'High' => 'широкий', 'HighBW' => 'Широкий канал', 'Home' => 'Домой', + 'Hostname' => 'Имя хоста', // Edited - 2019-03-25 'Hour' => 'Час', 'Hue' => 'Оттенок', 'Id' => 'ИД', @@ -384,13 +407,13 @@ $SLANG = array( 'Ignore' => 'Игнорировать', 'Image' => 'Изображение', 'ImageBufferSize' => 'Буфер изображений', - 'Images' => 'Images', + 'Images' => 'Изображения', // Added - 2019-03-25 'In' => 'In', 'Include' => 'Включить', 'Inverted' => 'Инвертировать', 'Iris' => 'Наклон', 'KeyString' => 'Key String', - 'Label' => 'Label', + 'Label' => 'Имя', // Added - 2019-03-25 'Language' => 'Язык', 'Last' => 'Последний', 'Layout' => 'Раскладка', // Added - 2009-02-08 @@ -401,15 +424,16 @@ $SLANG = array( 'Line' => 'Строка', // Added - 2011-06-16 'LinkedMonitors' => 'Привязанные мониторы', 'List' => 'Список', + 'ListMatches' => 'Список совпадений', // Edited - 2019-03-25 'Load' => 'Нагрузка', 'Local' => 'Локальный', 'Log' => 'Лог', // Added - 2011-06-16; 'LoggedInAs' => 'Пользователь', - 'Logging' => 'Logging', // Added - 2011-06-16 + 'Logging' => 'Логгирование', // Added - 2019-03-24 'LoggingIn' => 'Вход в систему', 'Login' => 'Войти', 'Logout' => 'Выйти', - 'Logs' => 'Logs', // Added - 2011-06-17 + 'Logs' => 'Логи', // Added - 2019-03-24 'Low' => 'узкий', 'LowBW' => 'Узкий канал', 'Main' => 'Основные', @@ -417,7 +441,7 @@ $SLANG = array( 'Manual' => 'Manual', 'Mark' => 'Метка', 'Max' => 'Макс.', - 'MaxBandwidth' => 'Max Bandwidth', + 'MaxBandwidth' => 'Макс. пропускная способность', // Added - 2019-03-25 'MaxBrScore' => 'Макс.
оценка', 'MaxFocusRange' => 'Макс. диап. фокуса', 'MaxFocusSpeed' => 'Макс. скор. фокуса', @@ -487,6 +511,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'В этом списке показаны найденные аналоговые и сетевые камеры, как уже заведенные, так и доступные для выбора.

Выберите нужную из списка ниже.

Обратите внимание, что не все камеры могут быть найдены, и что выбор камеры может переписать настройки определенные для этого монитора.

', // Added - 2009-03-31 'Monitors' => 'Мониторы', 'Montage' => 'Монтаж', + 'MontageReview' => 'Обзор монтажа', // Added - 2019-03-24 'Month' => 'Месяц', 'More' => 'Еще', // Added - 2011-06-16 'MotionFrameSkip' => 'Кол-во пропуск. кадров движения', @@ -506,15 +531,16 @@ $SLANG = array( 'Network' => 'Сеть', 'New' => 'Нов.', 'NewGroup' => 'Новая группа', - 'NewLabel' => 'New Label', + 'NewLabel' => 'Новое имя', // Added - 2019-03-25 'NewPassword' => 'Новый пароль', 'NewState' => 'Новое состояние', 'NewUser' => 'Новый пользователь', 'Next' => 'След.', 'No' => 'Нет', - 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 + 'NoDetectedCameras' => 'Нет камер', // Added - 2019-03-24 + 'NoDetectedProfiles' => 'Нет профилей', // Added - 2019-03-24 'NoFramesRecorded' => 'Это событие не содержит кадров', - 'NoGroup' => 'No Group', + 'NoGroup' => 'Нет группы', // Added - 2019-03-24 'NoSavedFilters' => 'нет сохраненных фильтров', 'NoStatisticsRecorded' => 'Статистика по этому событию/кадру не записана', 'None' => 'отсутствует', @@ -522,8 +548,8 @@ $SLANG = array( 'Normal' => 'Нормальная', 'Notes' => 'Примечание', 'NumPresets' => 'Кол-во предустановок', - 'Off' => 'Off', - 'On' => 'On', + 'Off' => 'Выкл.', // Added - 2019-03-25 + 'On' => 'Вкл.', // Added - 2019-03-25 'OnvifCredentialsIntro'=> 'Пожалуйста укажите имя пользователя и пароль для выбранной камеры.

Если пользователь для камеры не был создан, тогда будет создан новый с указанными данными.

', // Added - 2015-04-18 'OnvifProbe' => 'ONVIF', // Added - 2015-04-18 'OnvifProbeIntro' => 'В этом списке показаны найденные ONVIF камеры, как уже заведенные, так и доступные для выбора.

Выберите нужную из списка ниже.

Обратите внимание, что не все камеры могут быть найдены, и что выбор камеры может переписать настройки определенные для этого монитора.

', // Added - 2015-04-18 @@ -531,6 +557,8 @@ $SLANG = array( 'OpGt' => 'больше', 'OpGtEq' => 'больше либо равно', 'OpIn' => 'в списке', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'меньше', 'OpLtEq' => 'меньше или равно', 'OpMatches' => 'совпадает', @@ -540,6 +568,7 @@ $SLANG = array( 'Open' => 'Открыть', 'OptionHelp' => 'Справка', 'OptionRestartWarning' => 'Эти изменения подействуют только после перезапуска программы.', + 'OptionalEncoderParam' => 'Необязательные параметры кодировщика', // Added - 2019-03-24 'Options' => 'Опции', 'OrEnterNewName' => 'или введите новое имя', 'Order' => 'Сортировка', @@ -556,7 +585,6 @@ $SLANG = array( 'PasswordsDifferent' => 'Пароли не совпадают', 'Paths' => 'Пути', 'Pause' => 'Пауза', - 'Paused' => 'Пауза', 'Phone' => 'Phone', 'PhoneBW' => 'Телефонная линия', 'Pid' => 'PID', // Added - 2011-06-16 @@ -565,7 +593,7 @@ $SLANG = array( 'Play' => 'Играть', 'PlayAll' => 'Воспр. все', 'PleaseWait' => 'Пожалуйста подождите', - 'Plugins' => 'Plugins', + 'Plugins' => 'Плагины', // Edited - 2019-03-24 'Point' => 'Точка', 'PostEventImageBuffer' => 'Буфер после события', 'PreEventImageBuffer' => 'Буфер до события', @@ -578,9 +606,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'В этом списке показаны существующие профили потока выбранной камеры.

Выберите нужный из списка ниже.

Обратите внимание, что ZoneMinder не может добавить дополнительный профиль, и что выбор профиля может переписать настройки определенные для этого монитора.

', // Added - 2015-04-18 'Progress' => 'Прогресс', // Added - 2015-04-18 'Protocol' => 'Протокол', + 'RTSPDescribe' => 'Использовать RTSP URL для ответа', // Edited - 2019-03-25 + 'RTSPTransport' => 'Транспортный протокол RTSP', // Edited - 2019-03-25 'Rate' => 'Скорость', 'Real' => 'Реальная', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Record', + 'RecordAudio' => 'Сохранять ли аудиопоток при сохранении события.', // Edited - 2019-03-25 'RefImageBlendPct' => 'Смешение опорного кадра, %', 'Refresh' => 'Обновить', 'Remote' => 'Удаленный', @@ -596,6 +628,7 @@ $SLANG = array( 'ReplayAll' => 'Все события', 'ReplayGapless' => 'События подряд', 'ReplaySingle' => 'Одно событие', + 'ReportEventAudit' => 'Отчёт о событиях аудита', // Edited - 2019-03-24 'Reset' => 'Сбросить', 'ResetEventCounts' => 'Обнулить счетчик событий', 'Restart' => 'Перезапустить', @@ -607,13 +640,14 @@ $SLANG = array( 'Rewind' => 'Назад', 'RotateLeft' => 'Повернуть влево', 'RotateRight' => 'Повернуть вправо', - 'RunLocalUpdate' => 'Please run zmupdate.pl to update', // Added - 2011-05-25 + 'RunLocalUpdate' => 'Запустите zmupdate.pl для обновления', // Edited - 2019-03-24 'RunMode' => 'Режим работы', 'RunState' => 'Состояние', 'Running' => 'Выполняется', 'Save' => 'Сохранить', 'SaveAs' => 'Сохранить как', 'SaveFilter' => 'Сохранить фильтр', + 'SaveJPEGs' => 'Сохранить JPEG-и', // Edited - 2019-03-24 'Scale' => 'Масштаб', 'Score' => 'Оценка', 'Secs' => 'Сек.', @@ -621,45 +655,51 @@ $SLANG = array( 'Select' => 'Выбор', 'SelectFormat' => 'Выберите формат', // Added - 2011-06-17 'SelectLog' => 'Выберите лог', // Added - 2011-06-17 - 'SelectMonitors' => 'Select Monitors', + 'SelectMonitors' => 'Выбрать Мониторы', // Edited - 2019-03-24 'SelfIntersecting' => 'Polygon edges must not intersect', - 'Server' => 'Сервер', - 'Set' => 'Set', + 'Set' => 'Установка', // Edited - 2019-03-24 'SetNewBandwidth' => 'Установка новой ширина канала', - 'SetPreset' => 'Set Preset', + 'SetPreset' => 'Установка пресета', // Edited - 2019-03-24 'Settings' => 'Настройки', 'ShowFilterWindow' => 'Показать окно фильтра', 'ShowTimeline' => 'Показать график', 'SignalCheckColour' => 'Цвет проверки сигнала', - 'Size' => 'Size', - 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 + 'Size' => 'Размер', // Edited - 2019-03-24 + 'SkinDescription' => 'Смена стандартного скина для данного компьютера', // Edited - 2019-03-24 'Sleep' => 'Sleep', 'SortAsc' => 'По возр.', 'SortBy' => 'Сортировать', 'SortDesc' => 'По убыв.', 'Source' => 'Источник', - 'SourceColours' => 'Source Colours', // Added - 2009-02-08 + 'SourceColours' => 'Цвета источника', // Edited - 2019-03-24 'SourcePath' => 'Путь к источнику', // Added - 2009-02-08 'SourceType' => 'Тип источника', - 'Speed' => 'Speed', - 'SpeedHigh' => 'High Speed', - 'SpeedLow' => 'Low Speed', - 'SpeedMedium' => 'Medium Speed', - 'SpeedTurbo' => 'Turbo Speed', + 'Speed' => 'Скорость', //Edited - 2019-03-24 + 'SpeedHigh' => 'Высокая скорость', //Edited - 2019-03-24 + 'SpeedLow' => 'Низкая скорость', //Edited - 2019-03-24 + 'SpeedMedium' => 'Средняя скорость', + 'SpeedTurbo' => 'Максимальная скорость', // Edited - 2019-03-24 'Start' => 'Запустить', 'State' => 'Состояние', 'Stats' => 'Статистика', 'Status' => 'Статус', + 'StatusConnected' => 'Записывается', // Edited - 2019-03-25 + 'StatusNotRunning' => 'Не запущен', // Edited - 2019-03-25 + 'StatusRunning' => 'Не записывается', // Edited - 2019-03-25 + 'StatusUnknown' => 'Неизвестно', // Edited - 2019-03-25 'Step' => 'Шаг', 'StepBack' => 'Кадр назад', 'StepForward' => 'Кадр вперед', - 'StepLarge' => 'Large Step', - 'StepMedium' => 'Medium Step', - 'StepNone' => 'No Step', - 'StepSmall' => 'Small Step', + 'StepLarge' => 'Большой шаг', // Added - 2019-03-25 + 'StepMedium' => 'Средний шаг', // Added - 2019-03-25 + 'StepNone' => 'Без шагов', // Added - 2019-03-25 + 'StepSmall' => 'Малый шаг', // Added - 2019-03-25 'Stills' => 'Стоп-кадры', 'Stop' => 'Остановить', 'Stopped' => 'Остановлен', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Поток', 'StreamReplayBuffer' => 'Буфер потока повторного воспр.', 'Submit' => 'Применить', @@ -679,9 +719,9 @@ $SLANG = array( 'TimelineTip4' => 'Используйте кнопки снизу для отдаления и перемещения по временной шкале.', // Added 2013.08.15. 'Timestamp' => 'Метка времени', 'TimestampLabelFormat' => 'Формат метки', + 'TimestampLabelSize' => 'Размер метки', 'TimestampLabelX' => 'X-координата метки', 'TimestampLabelY' => 'Y-координата метки', - 'TimestampLabelSize' => 'Размер метки', 'Today' => 'Сегодня', 'Tools' => 'Инструменты', 'Total' => 'Всего', // Added - 2011-06-16 @@ -719,22 +759,24 @@ $SLANG = array( 'VersionRemindNever' => 'Не говорить о новых версиях', 'VersionRemindWeek' => 'Напомнить через неделю', 'Video' => 'Видео', - 'VideoFormat' => 'Video Format', + 'VideoFormat' => 'Формат видео', // Edited - 2019-03-24 'VideoGenFailed' => 'Ошибка генерации видео!', 'VideoGenFiles' => 'Existing Video Files', - 'VideoGenNoFiles' => 'No Video Files Found', + 'VideoGenNoFiles' => 'Видео не найдено', // Edited - 2019-03-24 'VideoGenParms' => 'Параметры генерации видео', - 'VideoGenSucceeded' => 'Video Generation Succeeded!', + 'VideoGenSucceeded' => 'Видео сгенерировано!', // Edited - 2019-03-24 'VideoSize' => 'Размер изображения', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Просмотр', 'ViewAll' => 'Просм. все', - 'ViewEvent' => 'View Event', + 'ViewEvent' => 'Просм. событие', // Edited - 2019-03-24 'ViewPaged' => 'Просм. постранично', 'Wake' => 'Wake', 'WarmupFrames' => 'Кадры разогрева', 'Watch' => 'Watch', 'Web' => 'Интерфейс', 'WebColour' => 'Цвет отметки', + 'WebSiteUrl' => 'URL сайта', // Edited - 2019-03-25 'Week' => 'Неделя', 'White' => 'Бал. белого', 'WhiteBalance' => 'White Balance', @@ -757,7 +799,7 @@ $SLANG = array( 'ZoneMinMaxBlobs' => 'Мин/Макс кол-во объектов', 'ZoneMinMaxFiltArea' => 'Мин/Макс разм. фильтр. зоны ', 'ZoneMinMaxPixelThres' => 'Мин/Макс порог изм. пикс. (0-255)', - 'ZoneMinderLog' => 'ZoneMinder Log', // Added - 2011-06-17 + 'ZoneMinderLog' => 'Лог ZoneMinder', // Edited - 2019-03-25 'ZoneOverloadFrames' => 'Кол-во игнор. кадров перегрузки', 'Zones' => 'Зоны', 'Zoom' => 'Увеличение', diff --git a/web/lang/se_se.php b/web/lang/se_se.php index e624e5696..53dd25178 100644 --- a/web/lang/se_se.php +++ b/web/lang/se_se.php @@ -79,6 +79,8 @@ $SLANG = array( 'Actual' => 'Verklig', 'AddNewControl' => 'Ny kontroll', 'AddNewMonitor' => 'Ny bevakare', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Ny användare', 'AddNewZone' => 'Ny zon', 'Alarm' => 'Larm', @@ -106,22 +108,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Arkivstatus', 'AttrAvgScore' => 'Ung. värde', 'AttrCause' => 'Orsak', - 'AttrDate' => 'Datum', - 'AttrDateTime' => 'Datum/Tid', 'AttrDiskBlocks' => 'Diskblock', 'AttrDiskPercent' => 'Diskprocent', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Längd', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Ramar', 'AttrId' => 'Id', 'AttrMaxScore' => 'Max. värde', 'AttrMonitorId' => 'Bevakningsid', 'AttrMonitorName' => 'Bevakningsnamn', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Namn', 'AttrNotes' => 'Notering', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'Systemlast', - 'AttrTime' => 'Tid', 'AttrTotalScore' => 'Totalvärde', - 'AttrWeekday' => 'Veckodag', 'Auto' => 'Automatik', 'AutoStopTimeout' => 'Tidsutlösning för automatstop', 'Available' => 'Available', // Added - 2009-03-31 @@ -154,9 +166,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Mixprocenten för referensen måste hara ett positivt heltal', 'BadSectionLength' => 'Sektionslängden måste vara ett heltal på minst 30 eller högre', 'BadSignalCheckColour' => 'Kontrollfärgen på signalen måste vara en giltig RGB färgsträng', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Buffern för strömmande uppspelning måste vara ett heltal på 0 eller högre', 'BadWarmupCount' => 'Uppvärmingsramen måste vara ett heltal på 0 eller högre', 'BadWebColour' => 'Webbfärgen måste vara en giltig sträng för webbfärg', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Bredden måste sättas til ett giltigt värde', 'Bandwidth' => 'Bandbredd', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -192,6 +206,7 @@ $SLANG = array( 'CanMoveRel' => 'Har relativ förflyttning', 'CanPan' => 'Har panorering', 'CanReset' => 'Har återställning', + 'CanReboot' => 'Can Reboot', 'CanSetPresets' => 'Har förinställningar', 'CanSleep' => 'Kan vila', 'CanTilt' => 'Kan tilta', @@ -220,10 +235,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Välj standard', 'Clear' => 'Clear', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Stäng', 'Colour' => 'Färg', 'Command' => 'Kommando', 'Component' => 'Component', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Konfigurera', 'ConfiguredFor' => 'Konfigurerad för', 'ConfirmDeleteEvents' => 'Är du säker på att du vill ta bort dom valda händelserna?', @@ -273,7 +290,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'Var vänlig och donera', 'DonateAlready' => 'Nej, Jag har redan donerat', - 'DonateEnticement' => 'Du har kört ZoneMinder ett tag nu och förhoppningsvis har du sett att det fungerar bra hemma eller på ditt företag. Även om ZoneMinder är, och kommer att vara, fri programvara och öppen kallkod, så kostar det pengar att utveckla och underhålla. Om du vill hjälpa till med framtida utveckling och nya funktioner så var vanlig och bidrag med en slant. Bidragen är naturligtvis en option men mycket uppskattade och du kan bidra med precis hur mycket du vill.

Om du vill ge ett bidrag väljer du nedan eller surfar till http://www.zoneminder.com/donate.html.

Tack för att du använder ZoneMinder, glöm inte att besöka forumen på ZoneMinder.com för support och förslag om hur du får din ZoneMinder att fungera lite bättre.', + 'DonateEnticement' => 'Du har kört ZoneMinder ett tag nu och förhoppningsvis har du sett att det fungerar bra hemma eller på ditt företag. Även om ZoneMinder är, och kommer att vara, fri programvara och öppen kallkod, så kostar det pengar att utveckla och underhålla. Om du vill hjälpa till med framtida utveckling och nya funktioner så var vanlig och bidrag med en slant. Bidragen är naturligtvis en option men mycket uppskattade och du kan bidra med precis hur mycket du vill.

Om du vill ge ett bidrag väljer du nedan eller surfar till https://zoneminder.com/donate/.

Tack för att du använder ZoneMinder, glöm inte att besöka forumen på ZoneMinder.com för support och förslag om hur du får din ZoneMinder att fungera lite bättre.', 'DonateRemindDay' => 'Inte än, påminn om 1 dag', 'DonateRemindHour' => 'Inte än, påminn om en 1 timme', 'DonateRemindMonth' => 'Inte än, påminn om 1 månad', @@ -281,9 +298,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Inte än, påminn om 1 vecka', 'DonateYes' => 'Ja, jag vill gärna donera nu', 'Download' => 'Ladda ner', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 'Duration' => 'Längd', 'Edit' => 'Redigera', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'E-post', 'EnableAlarms' => 'Aktivera larm', 'Enabled' => 'Aktiverad', @@ -300,6 +319,7 @@ $SLANG = array( 'Events' => 'Händelser', 'Exclude' => 'Exkludera', 'Execute' => 'Utför', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Exportera', 'ExportDetails' => 'Exportera händelsedetaljer', 'ExportFailed' => 'Exporten misslyckades', @@ -329,8 +349,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Utför kommando på alla träffar', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Meddela detaljer om alla träffar', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filter Px', 'FilterUnset' => 'Du måste specificera filtrets bredd och höjd', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Ladda upp alla träffar', 'FilterVideoEvents' => 'Skapa video för alla träffar', 'Filters' => 'Filter', @@ -355,6 +377,7 @@ $SLANG = array( 'Function' => 'Funktion', 'Gain' => 'Nivå', 'General' => 'Generell', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Skapa video', 'GeneratingVideo' => 'Skapar video', 'GoToZoneMinder' => 'Gå till ZoneMinder.com', @@ -375,6 +398,7 @@ $SLANG = array( 'High' => 'Hög', 'HighBW' => 'Hög bandbredd', 'Home' => 'Hem', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Timme', 'Hue' => 'Hue', 'Id' => 'nr', @@ -399,6 +423,7 @@ $SLANG = array( 'Line' => 'Line', // Added - 2011-06-16 'LinkedMonitors' => 'Länkade övervakare', 'List' => 'Lista', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Belastning', 'Local' => 'Lokal', 'Log' => 'Log', // Added - 2011-06-16 @@ -485,6 +510,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 'Monitors' => 'Bevakare', 'Montage' => 'Montera', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Månad', 'More' => 'More', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -511,6 +537,7 @@ $SLANG = array( 'Next' => 'Nästa', 'No' => 'Nej', 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Det finns inga ramar inspelade för denna händelse', 'NoGroup' => 'Ingen grupp', 'NoSavedFilters' => 'Inga sparade filter', @@ -529,6 +556,8 @@ $SLANG = array( 'OpGt' => 'större än', 'OpGtEq' => 'större än eller lika med', 'OpIn' => 'in set', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'mindre än', 'OpLtEq' => 'mindre än eller lika med', 'OpMatches' => 'matchar', @@ -538,6 +567,7 @@ $SLANG = array( 'Open' => 'Öppna', 'OptionHelp' => 'Optionhjälp', 'OptionRestartWarning' => 'Dessa ändringar kommer inte att vara implementerade\nnär systemet körs. När du är klar starta om\n ZoneMinder.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Alternativ', 'OrEnterNewName' => 'eller skriv in nytt namn', 'Order' => 'Sortera', @@ -575,9 +605,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protokol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Hastighet', 'Real' => 'Verklig', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Spela in', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Reference Image Blend %ge', 'Refresh' => 'Uppdatera', 'Remote' => 'Fjärr', @@ -593,6 +627,7 @@ $SLANG = array( 'ReplayAll' => 'Alla händelser', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Ensam händelse', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Återställ', 'ResetEventCounts' => 'Återställ händelseräknare', 'Restart' => 'Återstart', @@ -611,6 +646,7 @@ $SLANG = array( 'Save' => 'Spara', 'SaveAs' => 'Spara som', 'SaveFilter' => 'Spara filter', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Skala', 'Score' => 'Resultat', 'Secs' => 'Sek', @@ -627,6 +663,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Visa fönsterfilter', 'ShowTimeline' => 'Visa tidslinje', 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Storlek', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 'Sleep' => 'Vila', @@ -646,6 +683,10 @@ $SLANG = array( 'State' => 'Läge', 'Stats' => 'Statistik', 'Status' => 'Status', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Steg', 'StepBack' => 'Stepga bakåt', 'StepForward' => 'Stega framåt', @@ -656,6 +697,8 @@ $SLANG = array( 'Stills' => 'Stillbilder', 'Stop' => 'Stopp', 'Stopped' => 'Stoppad', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Strömmande', 'StreamReplayBuffer' => 'Buffert för strömmande uppspelning', 'Submit' => 'Skicka', @@ -675,6 +718,7 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => 'Tidsstämpel', 'TimestampLabelFormat' => 'Format på tidsstämpel', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'Värde på tidsstämpel X', 'TimestampLabelY' => 'Värde på tidsstämpel Y', 'Today' => 'Idag', @@ -721,6 +765,7 @@ $SLANG = array( 'VideoGenParms' => 'Inställningar för videogenerering', 'VideoGenSucceeded' => 'Videogenereringen lyckades!', 'VideoSize' => 'Videostorlek', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Visa', 'ViewAll' => 'Visa alla', 'ViewEvent' => 'Visa händelse', @@ -730,6 +775,7 @@ $SLANG = array( 'Watch' => 'Se', 'Web' => 'Webb', 'WebColour' => 'Webbfärg', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Vecka', 'White' => 'Vit', 'WhiteBalance' => 'Vitbalans', diff --git a/web/robots.txt b/web/robots.txt index 1f53798bb..9bef8dea6 100644 --- a/web/robots.txt +++ b/web/robots.txt @@ -1,2 +1,3 @@ User-agent: * Disallow: / +### \ No newline at end of file diff --git a/web/skins/classic/css/base/jquery-ui-theme.css b/web/skins/classic/css/base/jquery-ui-theme.css deleted file mode 100644 index c50986c55..000000000 --- a/web/skins/classic/css/base/jquery-ui-theme.css +++ /dev/null @@ -1,410 +0,0 @@ -/*! - * jQuery UI CSS Framework 1.11.3 - * http://jqueryui.com - * - * Copyright jQuery Foundation and other contributors - * Released under the MIT license. - * http://jquery.org/license - * - * http://api.jqueryui.com/category/theming/ - * - * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Arial%2CHelvetica%2Csans-serif&fsDefault=1em&fwDefault=normal&cornerRadius=3px&bgColorHeader=e9e9e9&bgTextureHeader=flat&borderColorHeader=dddddd&fcHeader=333333&iconColorHeader=444444&bgColorContent=ffffff&bgTextureContent=flat&borderColorContent=dddddd&fcContent=333333&iconColorContent=444444&bgColorDefault=f6f6f6&bgTextureDefault=flat&borderColorDefault=c5c5c5&fcDefault=454545&iconColorDefault=777777&bgColorHover=ededed&bgTextureHover=flat&borderColorHover=cccccc&fcHover=2b2b2b&iconColorHover=555555&bgColorActive=007fff&bgTextureActive=flat&borderColorActive=003eff&fcActive=ffffff&iconColorActive=ffffff&bgColorHighlight=fffa90&bgTextureHighlight=flat&borderColorHighlight=dad55e&fcHighlight=777620&iconColorHighlight=777620&bgColorError=fddfdf&bgTextureError=flat&borderColorError=f1a899&fcError=5f3f3f&iconColorError=cc0000&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=666666&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=5px&offsetTopShadow=0px&offsetLeftShadow=0px&cornerRadiusShadow=8px - */ - - -/* Component containers -----------------------------------*/ -.ui-widget { - font-family: Arial,Helvetica,sans-serif; - font-size: 1em; -} -.ui-widget .ui-widget { - font-size: 1em; -} -.ui-widget input, -.ui-widget select, -.ui-widget textarea, -.ui-widget button { - font-family: Arial,Helvetica,sans-serif; - font-size: 1em; -} -.ui-widget-content { - border: 1px solid #dddddd; - background: #ffffff; - color: #333333; -} -.ui-widget-content a { - color: #333333; -} -.ui-widget-header { - border: 1px solid #dddddd; - background: #e9e9e9; - color: #333333; - font-weight: bold; -} -.ui-widget-header a { - color: #333333; -} - -/* Interaction states -----------------------------------*/ -.ui-state-default, -.ui-widget-content .ui-state-default, -.ui-widget-header .ui-state-default { - border: 1px solid #c5c5c5; - background: #f6f6f6; - font-weight: normal; - color: #454545; -} -.ui-state-default a, -.ui-state-default a:link, -.ui-state-default a:visited { - color: #454545; - text-decoration: none; -} -.ui-state-hover, -.ui-widget-content .ui-state-hover, -.ui-widget-header .ui-state-hover, -.ui-state-focus, -.ui-widget-content .ui-state-focus, -.ui-widget-header .ui-state-focus { - border: 1px solid #cccccc; - background: #ededed; - font-weight: normal; - color: #2b2b2b; -} -.ui-state-hover a, -.ui-state-hover a:hover, -.ui-state-hover a:link, -.ui-state-hover a:visited, -.ui-state-focus a, -.ui-state-focus a:hover, -.ui-state-focus a:link, -.ui-state-focus a:visited { - color: #2b2b2b; - text-decoration: none; -} -.ui-state-active, -.ui-widget-content .ui-state-active, -.ui-widget-header .ui-state-active { - border: 1px solid #003eff; - background: #007fff; - font-weight: normal; - color: #ffffff; -} -.ui-state-active a, -.ui-state-active a:link, -.ui-state-active a:visited { - color: #ffffff; - text-decoration: none; -} - -/* Interaction Cues -----------------------------------*/ -.ui-state-highlight, -.ui-widget-content .ui-state-highlight, -.ui-widget-header .ui-state-highlight { - border: 1px solid #dad55e; - background: #fffa90; - color: #777620; -} -.ui-state-highlight a, -.ui-widget-content .ui-state-highlight a, -.ui-widget-header .ui-state-highlight a { - color: #777620; -} -.ui-state-error, -.ui-widget-content .ui-state-error, -.ui-widget-header .ui-state-error { - border: 1px solid #f1a899; - background: #fddfdf; - color: #5f3f3f; -} -.ui-state-error a, -.ui-widget-content .ui-state-error a, -.ui-widget-header .ui-state-error a { - color: #5f3f3f; -} -.ui-state-error-text, -.ui-widget-content .ui-state-error-text, -.ui-widget-header .ui-state-error-text { - color: #5f3f3f; -} -.ui-priority-primary, -.ui-widget-content .ui-priority-primary, -.ui-widget-header .ui-priority-primary { - font-weight: bold; -} -.ui-priority-secondary, -.ui-widget-content .ui-priority-secondary, -.ui-widget-header .ui-priority-secondary { - opacity: .7; - filter:Alpha(Opacity=70); /* support: IE8 */ - font-weight: normal; -} -.ui-state-disabled, -.ui-widget-content .ui-state-disabled, -.ui-widget-header .ui-state-disabled { - opacity: .35; - filter:Alpha(Opacity=35); /* support: IE8 */ - background-image: none; -} -.ui-state-disabled .ui-icon { - filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */ -} - -/* Icons -----------------------------------*/ - -/* states and images */ -.ui-icon { - width: 16px; - height: 16px; -} -.ui-icon, -.ui-widget-content .ui-icon { - background-image: url("../skins/classic/graphics/ui-icons_444444_256x240.png"); -} -.ui-widget-header .ui-icon { - background-image: url("../skins/classic/graphics/ui-icons_444444_256x240.png"); -} -.ui-state-default .ui-icon { - background-image: url("../skins/classic/graphics/ui-icons_777777_256x240.png"); -} -.ui-state-hover .ui-icon, -.ui-state-focus .ui-icon { - background-image: url("../skins/classic/graphics/ui-icons_555555_256x240.png"); -} -.ui-state-active .ui-icon { - background-image: url("../skins/classic/graphics/ui-icons_ffffff_256x240.png"); -} -.ui-state-highlight .ui-icon { - background-image: url("../skins/classic/graphics/ui-icons_777620_256x240.png"); -} -.ui-state-error .ui-icon, -.ui-state-error-text .ui-icon { - background-image: url("../skins/classic/graphics/ui-icons_cc0000_256x240.png"); -} - -/* positioning */ -.ui-icon-blank { background-position: 16px 16px; } -.ui-icon-carat-1-n { background-position: 0 0; } -.ui-icon-carat-1-ne { background-position: -16px 0; } -.ui-icon-carat-1-e { background-position: -32px 0; } -.ui-icon-carat-1-se { background-position: -48px 0; } -.ui-icon-carat-1-s { background-position: -64px 0; } -.ui-icon-carat-1-sw { background-position: -80px 0; } -.ui-icon-carat-1-w { background-position: -96px 0; } -.ui-icon-carat-1-nw { background-position: -112px 0; } -.ui-icon-carat-2-n-s { background-position: -128px 0; } -.ui-icon-carat-2-e-w { background-position: -144px 0; } -.ui-icon-triangle-1-n { background-position: 0 -16px; } -.ui-icon-triangle-1-ne { background-position: -16px -16px; } -.ui-icon-triangle-1-e { background-position: -32px -16px; } -.ui-icon-triangle-1-se { background-position: -48px -16px; } -.ui-icon-triangle-1-s { background-position: -64px -16px; } -.ui-icon-triangle-1-sw { background-position: -80px -16px; } -.ui-icon-triangle-1-w { background-position: -96px -16px; } -.ui-icon-triangle-1-nw { background-position: -112px -16px; } -.ui-icon-triangle-2-n-s { background-position: -128px -16px; } -.ui-icon-triangle-2-e-w { background-position: -144px -16px; } -.ui-icon-arrow-1-n { background-position: 0 -32px; } -.ui-icon-arrow-1-ne { background-position: -16px -32px; } -.ui-icon-arrow-1-e { background-position: -32px -32px; } -.ui-icon-arrow-1-se { background-position: -48px -32px; } -.ui-icon-arrow-1-s { background-position: -64px -32px; } -.ui-icon-arrow-1-sw { background-position: -80px -32px; } -.ui-icon-arrow-1-w { background-position: -96px -32px; } -.ui-icon-arrow-1-nw { background-position: -112px -32px; } -.ui-icon-arrow-2-n-s { background-position: -128px -32px; } -.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } -.ui-icon-arrow-2-e-w { background-position: -160px -32px; } -.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } -.ui-icon-arrowstop-1-n { background-position: -192px -32px; } -.ui-icon-arrowstop-1-e { background-position: -208px -32px; } -.ui-icon-arrowstop-1-s { background-position: -224px -32px; } -.ui-icon-arrowstop-1-w { background-position: -240px -32px; } -.ui-icon-arrowthick-1-n { background-position: 0 -48px; } -.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } -.ui-icon-arrowthick-1-e { background-position: -32px -48px; } -.ui-icon-arrowthick-1-se { background-position: -48px -48px; } -.ui-icon-arrowthick-1-s { background-position: -64px -48px; } -.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } -.ui-icon-arrowthick-1-w { background-position: -96px -48px; } -.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } -.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } -.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } -.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } -.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } -.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } -.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } -.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } -.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } -.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } -.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } -.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } -.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } -.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } -.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } -.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } -.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } -.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } -.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } -.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } -.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } -.ui-icon-arrow-4 { background-position: 0 -80px; } -.ui-icon-arrow-4-diag { background-position: -16px -80px; } -.ui-icon-extlink { background-position: -32px -80px; } -.ui-icon-newwin { background-position: -48px -80px; } -.ui-icon-refresh { background-position: -64px -80px; } -.ui-icon-shuffle { background-position: -80px -80px; } -.ui-icon-transfer-e-w { background-position: -96px -80px; } -.ui-icon-transferthick-e-w { background-position: -112px -80px; } -.ui-icon-folder-collapsed { background-position: 0 -96px; } -.ui-icon-folder-open { background-position: -16px -96px; } -.ui-icon-document { background-position: -32px -96px; } -.ui-icon-document-b { background-position: -48px -96px; } -.ui-icon-note { background-position: -64px -96px; } -.ui-icon-mail-closed { background-position: -80px -96px; } -.ui-icon-mail-open { background-position: -96px -96px; } -.ui-icon-suitcase { background-position: -112px -96px; } -.ui-icon-comment { background-position: -128px -96px; } -.ui-icon-person { background-position: -144px -96px; } -.ui-icon-print { background-position: -160px -96px; } -.ui-icon-trash { background-position: -176px -96px; } -.ui-icon-locked { background-position: -192px -96px; } -.ui-icon-unlocked { background-position: -208px -96px; } -.ui-icon-bookmark { background-position: -224px -96px; } -.ui-icon-tag { background-position: -240px -96px; } -.ui-icon-home { background-position: 0 -112px; } -.ui-icon-flag { background-position: -16px -112px; } -.ui-icon-calendar { background-position: -32px -112px; } -.ui-icon-cart { background-position: -48px -112px; } -.ui-icon-pencil { background-position: -64px -112px; } -.ui-icon-clock { background-position: -80px -112px; } -.ui-icon-disk { background-position: -96px -112px; } -.ui-icon-calculator { background-position: -112px -112px; } -.ui-icon-zoomin { background-position: -128px -112px; } -.ui-icon-zoomout { background-position: -144px -112px; } -.ui-icon-search { background-position: -160px -112px; } -.ui-icon-wrench { background-position: -176px -112px; } -.ui-icon-gear { background-position: -192px -112px; } -.ui-icon-heart { background-position: -208px -112px; } -.ui-icon-star { background-position: -224px -112px; } -.ui-icon-link { background-position: -240px -112px; } -.ui-icon-cancel { background-position: 0 -128px; } -.ui-icon-plus { background-position: -16px -128px; } -.ui-icon-plusthick { background-position: -32px -128px; } -.ui-icon-minus { background-position: -48px -128px; } -.ui-icon-minusthick { background-position: -64px -128px; } -.ui-icon-close { background-position: -80px -128px; } -.ui-icon-closethick { background-position: -96px -128px; } -.ui-icon-key { background-position: -112px -128px; } -.ui-icon-lightbulb { background-position: -128px -128px; } -.ui-icon-scissors { background-position: -144px -128px; } -.ui-icon-clipboard { background-position: -160px -128px; } -.ui-icon-copy { background-position: -176px -128px; } -.ui-icon-contact { background-position: -192px -128px; } -.ui-icon-image { background-position: -208px -128px; } -.ui-icon-video { background-position: -224px -128px; } -.ui-icon-script { background-position: -240px -128px; } -.ui-icon-alert { background-position: 0 -144px; } -.ui-icon-info { background-position: -16px -144px; } -.ui-icon-notice { background-position: -32px -144px; } -.ui-icon-help { background-position: -48px -144px; } -.ui-icon-check { background-position: -64px -144px; } -.ui-icon-bullet { background-position: -80px -144px; } -.ui-icon-radio-on { background-position: -96px -144px; } -.ui-icon-radio-off { background-position: -112px -144px; } -.ui-icon-pin-w { background-position: -128px -144px; } -.ui-icon-pin-s { background-position: -144px -144px; } -.ui-icon-play { background-position: 0 -160px; } -.ui-icon-pause { background-position: -16px -160px; } -.ui-icon-seek-next { background-position: -32px -160px; } -.ui-icon-seek-prev { background-position: -48px -160px; } -.ui-icon-seek-end { background-position: -64px -160px; } -.ui-icon-seek-start { background-position: -80px -160px; } -/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ -.ui-icon-seek-first { background-position: -80px -160px; } -.ui-icon-stop { background-position: -96px -160px; } -.ui-icon-eject { background-position: -112px -160px; } -.ui-icon-volume-off { background-position: -128px -160px; } -.ui-icon-volume-on { background-position: -144px -160px; } -.ui-icon-power { background-position: 0 -176px; } -.ui-icon-signal-diag { background-position: -16px -176px; } -.ui-icon-signal { background-position: -32px -176px; } -.ui-icon-battery-0 { background-position: -48px -176px; } -.ui-icon-battery-1 { background-position: -64px -176px; } -.ui-icon-battery-2 { background-position: -80px -176px; } -.ui-icon-battery-3 { background-position: -96px -176px; } -.ui-icon-circle-plus { background-position: 0 -192px; } -.ui-icon-circle-minus { background-position: -16px -192px; } -.ui-icon-circle-close { background-position: -32px -192px; } -.ui-icon-circle-triangle-e { background-position: -48px -192px; } -.ui-icon-circle-triangle-s { background-position: -64px -192px; } -.ui-icon-circle-triangle-w { background-position: -80px -192px; } -.ui-icon-circle-triangle-n { background-position: -96px -192px; } -.ui-icon-circle-arrow-e { background-position: -112px -192px; } -.ui-icon-circle-arrow-s { background-position: -128px -192px; } -.ui-icon-circle-arrow-w { background-position: -144px -192px; } -.ui-icon-circle-arrow-n { background-position: -160px -192px; } -.ui-icon-circle-zoomin { background-position: -176px -192px; } -.ui-icon-circle-zoomout { background-position: -192px -192px; } -.ui-icon-circle-check { background-position: -208px -192px; } -.ui-icon-circlesmall-plus { background-position: 0 -208px; } -.ui-icon-circlesmall-minus { background-position: -16px -208px; } -.ui-icon-circlesmall-close { background-position: -32px -208px; } -.ui-icon-squaresmall-plus { background-position: -48px -208px; } -.ui-icon-squaresmall-minus { background-position: -64px -208px; } -.ui-icon-squaresmall-close { background-position: -80px -208px; } -.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } -.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } -.ui-icon-grip-solid-vertical { background-position: -32px -224px; } -.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } -.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } -.ui-icon-grip-diagonal-se { background-position: -80px -224px; } - - -/* Misc visuals -----------------------------------*/ - -/* Corner radius */ -.ui-corner-all, -.ui-corner-top, -.ui-corner-left, -.ui-corner-tl { - border-top-left-radius: 3px; -} -.ui-corner-all, -.ui-corner-top, -.ui-corner-right, -.ui-corner-tr { - border-top-right-radius: 3px; -} -.ui-corner-all, -.ui-corner-bottom, -.ui-corner-left, -.ui-corner-bl { - border-bottom-left-radius: 3px; -} -.ui-corner-all, -.ui-corner-bottom, -.ui-corner-right, -.ui-corner-br { - border-bottom-right-radius: 3px; -} - -/* Overlays */ -.ui-widget-overlay { - background: #aaaaaa; - opacity: .3; - filter: Alpha(Opacity=30); /* support: IE8 */ -} -.ui-widget-shadow { - margin: 0px 0 0 0px; - padding: 5px; - background: #666666; - opacity: .3; - filter: Alpha(Opacity=30); /* support: IE8 */ - border-radius: 8px; -} diff --git a/web/skins/classic/css/base/skin.css b/web/skins/classic/css/base/skin.css index 3fdd5a12f..a963dc0e7 100644 --- a/web/skins/classic/css/base/skin.css +++ b/web/skins/classic/css/base/skin.css @@ -150,6 +150,7 @@ input,textarea,select,button,.btn-primary { font-weight: 400; font-size: 100%; color: #333333; + background-color: #f8f8f8; text-align: left; border-radius:4px; } @@ -341,17 +342,60 @@ fieldset > legend { /* * Behavior classes */ -.alarm, .errorText, .error { - color: #ff3f34; -} -.alert, .warnText, .warning { - color: #ffa801; -} .ok, .infoText { color: #0fb9b1; } +.alert, .warnText, .warning, .disabledText { + color: #ffa801; +} + + +.alarm, .errorText, .error { + color: #ff3f34; +} + +.timedErrorBox { + color:white; + background:#e74c3c; + border-radius:5px; + padding:5px; + -moz-animation: inAndOut 5s ease-in forwards; + -webkit-animation: inAndOut 5s ease-in forwards; + animation: inAndOut 5s ease-in forwards; +} + +/* + the timed classed auto disappear after 5s +*/ +.timedWarningBox { + color:white; + background:#e67e22; + border-radius:5px; + padding:5px; + -moz-animation: inAndOut 5s ease-in forwards; + -webkit-animation: inAndOut 5s ease-in forwards; + animation: inAndOut 5s ease-in forwards; +} + +.timedSuccessBox { + color:white; + background:#27ae60; + border-radius:5px; + padding:5px; + -moz-animation: inAndOut 5s ease-in forwards; + -webkit-animation: inAndOut 5s ease-in forwards; + animation: inAndOut 5s ease-in forwards; +} + +@keyframes inAndOut { + 0% {opacity:0;} + 10% {opacity:1;} + 90% {opacity:1;} + 100% {opacity:0;} +} + .fakelink { color: #7f7fb2; @@ -545,6 +589,7 @@ input[type=submit]:hover, button:disabled, input[type=button]:disabled, input[type=submit]:disabled, + a.disabled, .btn-primary:disabled { background-color: #aaaaaa; border-color: #bbbbbb; @@ -592,6 +637,7 @@ color:#ffa801; .container-fluid { position: relative; + padding-bottom: 10px; } .sidebar { @@ -655,3 +701,6 @@ li.search-choice { margin-bottom:5px; } +.form-check-inline { + display: inline-block; +} diff --git a/web/skins/classic/css/base/views/console.css b/web/skins/classic/css/base/views/console.css index 4a5470b6b..1b0e77b4f 100644 --- a/web/skins/classic/css/base/views/console.css +++ b/web/skins/classic/css/base/views/console.css @@ -75,7 +75,7 @@ .consoleTable .colMark { width: 52px; - text-align: center; + text-align: left; } .consoleTable .colEvents { diff --git a/web/skins/classic/css/base/views/control.css b/web/skins/classic/css/base/views/control.css index 90757a146..f0a26e763 100644 --- a/web/skins/classic/css/base/views/control.css +++ b/web/skins/classic/css/base/views/control.css @@ -1,7 +1,7 @@ .ptzControls { vertical-align: top; margin: 10px auto 0; - width: 520px; + width: 90%; } .ptzControls::after { @@ -15,6 +15,9 @@ .ptzControls input.ptzTextBtn { margin-top: 2px; } +.ptzControls button { + border: none; +} .ptzControls .controlsPanel { margin: 0 auto; @@ -25,11 +28,11 @@ } .ptzControls .controlsPanel .arrowControl { - width: 40px; + width: 60px; margin: 0 4px; } -.ptzControls .controlsPanel .arrowControl input { +.ptzControls .controlsPanel .arrowControl button.longArrowBtn { display: block; } @@ -69,10 +72,13 @@ .ptzControls .controlsPanel .irisControls { float: right; + text-align: center; } .ptzControls .controlsPanel .whiteControls { float: right; + width: 120px; + text-align: center; } .ptzControls .controlsPanel .pantiltPanel { @@ -87,8 +93,8 @@ border: 1px solid #006699; text-align: center; padding: 1px; - width: 102px; - height: 102px; + width: 100px; + height: 100px; } .ptzControls .controlsPanel .pantiltPanel .pantiltControls .arrowBtn { @@ -138,17 +144,17 @@ margin: 5px auto; } -.ptzControls .presetControls { - margin: 5px auto; +.ptzControls .presetControls div { + margin: 5px 200px 5px 180px; } -.ptzControls .presetControls input { +.ptzControls .presetControls button { margin: 1px; } -.ptzControls .presetControls input.ptzNumBtn { +.ptzControls .presetControls button.ptzNumBtn { padding: 1px 2px; - width: 24px; + width: 45px; color: #ffffff; text-align: center; } diff --git a/web/skins/classic/css/base/views/controlcap.css b/web/skins/classic/css/base/views/controlcap.css new file mode 100644 index 000000000..ed9a664e7 --- /dev/null +++ b/web/skins/classic/css/base/views/controlcap.css @@ -0,0 +1,4 @@ + +input[type="number"] { + width: 70px; +} diff --git a/web/skins/classic/css/base/views/controlcaps.css b/web/skins/classic/css/base/views/controlcaps.css index 1a2783fd7..d860d88ae 100644 --- a/web/skins/classic/css/base/views/controlcaps.css +++ b/web/skins/classic/css/base/views/controlcaps.css @@ -1,3 +1,9 @@ -#content table.major .colCanMove, #content table.major .colCanZoom, #content table.major .colCanFocus, #content table.major .colCanIris, #content table.major .colCanWhiteBal, #content table.major .colHasPresets { +#content table.major .colCanMove, +#content table.major .colCanZoom, +#content table.major .colCanFocus, +#content table.major .colCanIris, +#content table.major .colCanWhiteBal, +#content table.major .colHasPresets { text-align: center; } + diff --git a/web/skins/classic/css/base/views/event.css b/web/skins/classic/css/base/views/event.css index 4696215b7..f392cb99c 100644 --- a/web/skins/classic/css/base/views/event.css +++ b/web/skins/classic/css/base/views/event.css @@ -61,7 +61,7 @@ span.noneCue { } #menuBar1 #replayControl { - margin: 0 4px 0 auto; + margin: auto 5px auto auto; } #menuBar1 div { diff --git a/web/skins/classic/css/base/views/export.css b/web/skins/classic/css/base/views/export.css index e22851ca0..2b090a77a 100644 --- a/web/skins/classic/css/base/views/export.css +++ b/web/skins/classic/css/base/views/export.css @@ -1,4 +1,4 @@ -#contentTable + input { +input { margin-top: 6px; } @@ -13,3 +13,7 @@ #downloadLink { margin-top: 8px; } + +.form-group { + text-align: right; +} diff --git a/web/skins/classic/css/base/views/frame.css b/web/skins/classic/css/base/views/frame.css index c9cb1846c..947fee1bc 100644 --- a/web/skins/classic/css/base/views/frame.css +++ b/web/skins/classic/css/base/views/frame.css @@ -9,8 +9,3 @@ display: flex; justify-content: space-between; } - -#controls a { - width: 40px; - margin-left: -20px; -} diff --git a/web/skins/classic/css/base/views/monitor.css b/web/skins/classic/css/base/views/monitor.css index 7cac28fe1..217b2f374 100644 --- a/web/skins/classic/css/base/views/monitor.css +++ b/web/skins/classic/css/base/views/monitor.css @@ -3,3 +3,20 @@ margin-left: 3px; padding: 0px; } + +.SourceOptions input, +.SourcePath input { + width: 100%; +} + +textarea, +input[name="newMonitor[Name]"] { + width: 100%; +} +input[name="newMonitor[Width]"], +input[name="newMonitor[Height]"] { + width: 80px; +} +select.chosen { + width: 100%; +} diff --git a/web/skins/classic/css/base/views/options.css b/web/skins/classic/css/base/views/options.css index f7123086c..a60d98001 100644 --- a/web/skins/classic/css/base/views/options.css +++ b/web/skins/classic/css/base/views/options.css @@ -21,3 +21,8 @@ input.large { #contentTable.userTable .colMonitor, #contentTable.userTable .colUsername { text-align: left; } + +input[name="newConfig[ZM_OPT_GOOG_RECAPTCHA_SITEKEY]"], +input[name="newConfig[ZM_OPT_GOOG_RECAPTCHA_SECRETKEY]"] { + width: 100%; +} diff --git a/web/skins/classic/css/base/views/privacy.css b/web/skins/classic/css/base/views/privacy.css new file mode 100644 index 000000000..f368fa110 --- /dev/null +++ b/web/skins/classic/css/base/views/privacy.css @@ -0,0 +1,14 @@ +h6 { + text-align: left; + font-weight: bold; + text-decoration: underline; +} + +p { + text-align: left; +} + +ul { + text-align: left; + list-style-type: disc; +} diff --git a/web/skins/classic/css/base/views/storage.css b/web/skins/classic/css/base/views/storage.css index afb83e441..9a3c1bcdd 100644 --- a/web/skins/classic/css/base/views/storage.css +++ b/web/skins/classic/css/base/views/storage.css @@ -1,3 +1,5 @@ -input[type="url"] { +input[name="newStorage[Name]"], +input[name="newStorage[Path]"], +input[name="newStorage[Url]"] { width: 100%; } diff --git a/web/skins/classic/css/base/views/timeline.css b/web/skins/classic/css/base/views/timeline.css index 4e78e82cf..c2efed713 100644 --- a/web/skins/classic/css/base/views/timeline.css +++ b/web/skins/classic/css/base/views/timeline.css @@ -48,20 +48,24 @@ } #topPanel #image { - float: right; margin: 0 auto; - width: 90%; text-align: right; margin-right: 2px; background-color: #eeeeee; border: 1px solid #c8c8c8; + position: relative; + width: 100%; } #topPanel #image img { - float: left; - top: 0px; - background-color: #f8f8f8; - width: 100%; + width: auto; + max-width: 100%; + height: 100%; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; } #topPanel #dataPanel { @@ -97,6 +101,7 @@ } #chartPanel { + clear: both; position: relative; margin: 0 auto; } @@ -110,22 +115,25 @@ #chartPanel #activity { position: absolute; + top: 0; + left: 0; text-align: center; - left: 0px; border-bottom: 1px solid #cccccc; + width: 100%; } -#chartPanel #activity div.activity { +#chartPanel div.activity { position: absolute; - bottom: 0px; + bottom: 0; z-index: 3; - width: 1px; + width: 0.17%; } #chartPanel .events { position: absolute; text-align: center; - left: 0px; + left: 0; + width: 100%; background-color: #fcfcfc; border-bottom: 1px solid black; } @@ -159,8 +167,9 @@ span.keyEntry { } -img.keyBox { +div.keyBox { position: relative; + display: inline-block; border: 1px solid black; width: 16px; height: 16px; diff --git a/web/skins/classic/css/base/views/timeline.css.php b/web/skins/classic/css/base/views/timeline.css.php index 85078d107..a9d7be691 100644 --- a/web/skins/classic/css/base/views/timeline.css.php +++ b/web/skins/classic/css/base/views/timeline.css.php @@ -1,10 +1,8 @@ .chartSize { - width: px; height: px; } .graphSize { - width: px; height: px; } @@ -13,47 +11,55 @@ } .graphWidth { - width: px; } .imageSize { - width: px; - height: px; } .imageHeight { - height: px; + } .activitySize { - width: px; height: px; } .eventsSize { - width: px; height: px; } -.eventsHeight { +.events .event { height: px; } #chartPanel .eventsPos { top: px; } #chartPanel .activityPos { top: px; @@ -66,11 +72,10 @@ elseif ( $mode == "split" ) } } -foreach( array_keys($monEventSlots) as $monitorId ) -{ +foreach ( array_keys($monEventSlots) as $monitorId ) { ?> .monitorColour { - background-color: ; + background-color: WebColour() ?>; } px; } -.eventsHeight { +.events .event { height: px; } - -#chartPanel .eventsPos { - top: px; -} - -#chartPanel .activityPos { - top: px; -} - -#chartPanel .eventsPos { - top: px; -} - -.monitorColour { - background-color: ; -} - diff --git a/web/skins/classic/css/dark/jquery-ui-theme.css b/web/skins/classic/css/dark/jquery-ui-theme.css deleted file mode 100644 index 6288b262e..000000000 --- a/web/skins/classic/css/dark/jquery-ui-theme.css +++ /dev/null @@ -1,410 +0,0 @@ -/*! - * jQuery UI CSS Framework 1.11.4 - * http://jqueryui.com - * - * Copyright jQuery Foundation and other contributors - * Released under the MIT license. - * http://jquery.org/license - * - * http://api.jqueryui.com/category/theming/ - * - * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Arial%2CHelvetica%2Csans-serif&fsDefault=1em&fwDefault=normal&cornerRadius=2px&bgColorHeader=%23222222&bgTextureHeader=flat&borderColorHeader=%23222222&fcHeader=%23eeeeee&iconColorHeader=%23777777&bgColorContent=%23222222&bgTextureContent=flat&borderColorContent=%23dddddd&fcContent=%23dddddd&iconColorContent=%23444444&bgColorDefault=%23444444&bgTextureDefault=flat&borderColorDefault=%23444444&fcDefault=%23eeeeee&iconColorDefault=%23777777&bgColorHover=%23555555&bgTextureHover=flat&borderColorHover=%23444444&fcHover=%23eeeeee&iconColorHover=%23777777&bgColorActive=%23666666&bgTextureActive=flat&borderColorActive=%23666666&fcActive=%23eeeeee&iconColorActive=%23777777&bgColorHighlight=%23222222&bgTextureHighlight=flat&borderColorHighlight=%23777777&fcHighlight=%23eeeeee&iconColorHighlight=%23777777&bgColorError=%23fddfdf&bgTextureError=flat&borderColorError=%23f1a899&fcError=%235f3f3f&iconColorError=%23cc0000&bgColorOverlay=%23aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=%23666666&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=5px&offsetTopShadow=0px&offsetLeftShadow=0px&cornerRadiusShadow=8px&bgImgOpacityHeader=&bgImgOpacityContent=&bgImgOpacityDefault=&bgImgOpacityHover=&bgImgOpacityActive=&bgImgOpacityHighlight=&bgImgOpacityError= - */ - - -/* Component containers -----------------------------------*/ -.ui-widget { - font-family: Arial,Helvetica,sans-serif; - font-size: 1em; -} -.ui-widget .ui-widget { - font-size: 1em; -} -.ui-widget input, -.ui-widget select, -.ui-widget textarea, -.ui-widget button { - font-family: Arial,Helvetica,sans-serif; - font-size: 1em; -} -.ui-widget-content { - border: 1px solid #dddddd; - background: #222222; - color: #dddddd; -} -.ui-widget-content a { - color: #dddddd; -} -.ui-widget-header { - border: 1px solid #222222; - background: #222222; - color: #eeeeee; - font-weight: bold; -} -.ui-widget-header a { - color: #eeeeee; -} - -/* Interaction states -----------------------------------*/ -.ui-state-default, -.ui-widget-content .ui-state-default, -.ui-widget-header .ui-state-default { - border: 1px solid #444444; - background: #444444; - font-weight: normal; - color: #eeeeee; -} -.ui-state-default a, -.ui-state-default a:link, -.ui-state-default a:visited { - color: #eeeeee; - text-decoration: none; -} -.ui-state-hover, -.ui-widget-content .ui-state-hover, -.ui-widget-header .ui-state-hover, -.ui-state-focus, -.ui-widget-content .ui-state-focus, -.ui-widget-header .ui-state-focus { - border: 1px solid #444444; - background: #555555; - font-weight: normal; - color: #eeeeee; -} -.ui-state-hover a, -.ui-state-hover a:hover, -.ui-state-hover a:link, -.ui-state-hover a:visited, -.ui-state-focus a, -.ui-state-focus a:hover, -.ui-state-focus a:link, -.ui-state-focus a:visited { - color: #eeeeee; - text-decoration: none; -} -.ui-state-active, -.ui-widget-content .ui-state-active, -.ui-widget-header .ui-state-active { - border: 1px solid #666666; - background: #666666; - font-weight: normal; - color: #eeeeee; -} -.ui-state-active a, -.ui-state-active a:link, -.ui-state-active a:visited { - color: #eeeeee; - text-decoration: none; -} - -/* Interaction Cues -----------------------------------*/ -.ui-state-highlight, -.ui-widget-content .ui-state-highlight, -.ui-widget-header .ui-state-highlight { - border: 1px solid #777777; - background: #222222; - color: #eeeeee; -} -.ui-state-highlight a, -.ui-widget-content .ui-state-highlight a, -.ui-widget-header .ui-state-highlight a { - color: #eeeeee; -} -.ui-state-error, -.ui-widget-content .ui-state-error, -.ui-widget-header .ui-state-error { - border: 1px solid #f1a899; - background: #fddfdf; - color: #5f3f3f; -} -.ui-state-error a, -.ui-widget-content .ui-state-error a, -.ui-widget-header .ui-state-error a { - color: #5f3f3f; -} -.ui-state-error-text, -.ui-widget-content .ui-state-error-text, -.ui-widget-header .ui-state-error-text { - color: #5f3f3f; -} -.ui-priority-primary, -.ui-widget-content .ui-priority-primary, -.ui-widget-header .ui-priority-primary { - font-weight: bold; -} -.ui-priority-secondary, -.ui-widget-content .ui-priority-secondary, -.ui-widget-header .ui-priority-secondary { - opacity: .7; - filter:Alpha(Opacity=70); /* support: IE8 */ - font-weight: normal; -} -.ui-state-disabled, -.ui-widget-content .ui-state-disabled, -.ui-widget-header .ui-state-disabled { - opacity: .35; - filter:Alpha(Opacity=35); /* support: IE8 */ - background-image: none; -} -.ui-state-disabled .ui-icon { - filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */ -} - -/* Icons -----------------------------------*/ - -/* states and images */ -.ui-icon { - width: 16px; - height: 16px; -} -.ui-icon, -.ui-widget-content .ui-icon { - background-image: url("../skins/classic/graphics/ui-icons_444444_256x240.png"); -} -.ui-widget-header .ui-icon { - background-image: url("../skins/classic/graphics/ui-icons_777777_256x240.png"); -} -.ui-state-default .ui-icon { - background-image: url("../skins/classic/graphics/ui-icons_777777_256x240.png"); -} -.ui-state-hover .ui-icon, -.ui-state-focus .ui-icon { - background-image: url("../skins/classic/graphics/ui-icons_777777_256x240.png"); -} -.ui-state-active .ui-icon { - background-image: url("../skins/classic/graphics/ui-icons_777777_256x240.png"); -} -.ui-state-highlight .ui-icon { - background-image: url("../skins/classic/graphics/ui-icons_777777_256x240.png"); -} -.ui-state-error .ui-icon, -.ui-state-error-text .ui-icon { - background-image: url("../skins/classic/graphics/ui-icons_cc0000_256x240.png"); -} - -/* positioning */ -.ui-icon-blank { background-position: 16px 16px; } -.ui-icon-carat-1-n { background-position: 0 0; } -.ui-icon-carat-1-ne { background-position: -16px 0; } -.ui-icon-carat-1-e { background-position: -32px 0; } -.ui-icon-carat-1-se { background-position: -48px 0; } -.ui-icon-carat-1-s { background-position: -64px 0; } -.ui-icon-carat-1-sw { background-position: -80px 0; } -.ui-icon-carat-1-w { background-position: -96px 0; } -.ui-icon-carat-1-nw { background-position: -112px 0; } -.ui-icon-carat-2-n-s { background-position: -128px 0; } -.ui-icon-carat-2-e-w { background-position: -144px 0; } -.ui-icon-triangle-1-n { background-position: 0 -16px; } -.ui-icon-triangle-1-ne { background-position: -16px -16px; } -.ui-icon-triangle-1-e { background-position: -32px -16px; } -.ui-icon-triangle-1-se { background-position: -48px -16px; } -.ui-icon-triangle-1-s { background-position: -64px -16px; } -.ui-icon-triangle-1-sw { background-position: -80px -16px; } -.ui-icon-triangle-1-w { background-position: -96px -16px; } -.ui-icon-triangle-1-nw { background-position: -112px -16px; } -.ui-icon-triangle-2-n-s { background-position: -128px -16px; } -.ui-icon-triangle-2-e-w { background-position: -144px -16px; } -.ui-icon-arrow-1-n { background-position: 0 -32px; } -.ui-icon-arrow-1-ne { background-position: -16px -32px; } -.ui-icon-arrow-1-e { background-position: -32px -32px; } -.ui-icon-arrow-1-se { background-position: -48px -32px; } -.ui-icon-arrow-1-s { background-position: -64px -32px; } -.ui-icon-arrow-1-sw { background-position: -80px -32px; } -.ui-icon-arrow-1-w { background-position: -96px -32px; } -.ui-icon-arrow-1-nw { background-position: -112px -32px; } -.ui-icon-arrow-2-n-s { background-position: -128px -32px; } -.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } -.ui-icon-arrow-2-e-w { background-position: -160px -32px; } -.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } -.ui-icon-arrowstop-1-n { background-position: -192px -32px; } -.ui-icon-arrowstop-1-e { background-position: -208px -32px; } -.ui-icon-arrowstop-1-s { background-position: -224px -32px; } -.ui-icon-arrowstop-1-w { background-position: -240px -32px; } -.ui-icon-arrowthick-1-n { background-position: 0 -48px; } -.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } -.ui-icon-arrowthick-1-e { background-position: -32px -48px; } -.ui-icon-arrowthick-1-se { background-position: -48px -48px; } -.ui-icon-arrowthick-1-s { background-position: -64px -48px; } -.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } -.ui-icon-arrowthick-1-w { background-position: -96px -48px; } -.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } -.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } -.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } -.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } -.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } -.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } -.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } -.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } -.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } -.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } -.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } -.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } -.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } -.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } -.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } -.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } -.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } -.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } -.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } -.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } -.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } -.ui-icon-arrow-4 { background-position: 0 -80px; } -.ui-icon-arrow-4-diag { background-position: -16px -80px; } -.ui-icon-extlink { background-position: -32px -80px; } -.ui-icon-newwin { background-position: -48px -80px; } -.ui-icon-refresh { background-position: -64px -80px; } -.ui-icon-shuffle { background-position: -80px -80px; } -.ui-icon-transfer-e-w { background-position: -96px -80px; } -.ui-icon-transferthick-e-w { background-position: -112px -80px; } -.ui-icon-folder-collapsed { background-position: 0 -96px; } -.ui-icon-folder-open { background-position: -16px -96px; } -.ui-icon-document { background-position: -32px -96px; } -.ui-icon-document-b { background-position: -48px -96px; } -.ui-icon-note { background-position: -64px -96px; } -.ui-icon-mail-closed { background-position: -80px -96px; } -.ui-icon-mail-open { background-position: -96px -96px; } -.ui-icon-suitcase { background-position: -112px -96px; } -.ui-icon-comment { background-position: -128px -96px; } -.ui-icon-person { background-position: -144px -96px; } -.ui-icon-print { background-position: -160px -96px; } -.ui-icon-trash { background-position: -176px -96px; } -.ui-icon-locked { background-position: -192px -96px; } -.ui-icon-unlocked { background-position: -208px -96px; } -.ui-icon-bookmark { background-position: -224px -96px; } -.ui-icon-tag { background-position: -240px -96px; } -.ui-icon-home { background-position: 0 -112px; } -.ui-icon-flag { background-position: -16px -112px; } -.ui-icon-calendar { background-position: -32px -112px; } -.ui-icon-cart { background-position: -48px -112px; } -.ui-icon-pencil { background-position: -64px -112px; } -.ui-icon-clock { background-position: -80px -112px; } -.ui-icon-disk { background-position: -96px -112px; } -.ui-icon-calculator { background-position: -112px -112px; } -.ui-icon-zoomin { background-position: -128px -112px; } -.ui-icon-zoomout { background-position: -144px -112px; } -.ui-icon-search { background-position: -160px -112px; } -.ui-icon-wrench { background-position: -176px -112px; } -.ui-icon-gear { background-position: -192px -112px; } -.ui-icon-heart { background-position: -208px -112px; } -.ui-icon-star { background-position: -224px -112px; } -.ui-icon-link { background-position: -240px -112px; } -.ui-icon-cancel { background-position: 0 -128px; } -.ui-icon-plus { background-position: -16px -128px; } -.ui-icon-plusthick { background-position: -32px -128px; } -.ui-icon-minus { background-position: -48px -128px; } -.ui-icon-minusthick { background-position: -64px -128px; } -.ui-icon-close { background-position: -80px -128px; } -.ui-icon-closethick { background-position: -96px -128px; } -.ui-icon-key { background-position: -112px -128px; } -.ui-icon-lightbulb { background-position: -128px -128px; } -.ui-icon-scissors { background-position: -144px -128px; } -.ui-icon-clipboard { background-position: -160px -128px; } -.ui-icon-copy { background-position: -176px -128px; } -.ui-icon-contact { background-position: -192px -128px; } -.ui-icon-image { background-position: -208px -128px; } -.ui-icon-video { background-position: -224px -128px; } -.ui-icon-script { background-position: -240px -128px; } -.ui-icon-alert { background-position: 0 -144px; } -.ui-icon-info { background-position: -16px -144px; } -.ui-icon-notice { background-position: -32px -144px; } -.ui-icon-help { background-position: -48px -144px; } -.ui-icon-check { background-position: -64px -144px; } -.ui-icon-bullet { background-position: -80px -144px; } -.ui-icon-radio-on { background-position: -96px -144px; } -.ui-icon-radio-off { background-position: -112px -144px; } -.ui-icon-pin-w { background-position: -128px -144px; } -.ui-icon-pin-s { background-position: -144px -144px; } -.ui-icon-play { background-position: 0 -160px; } -.ui-icon-pause { background-position: -16px -160px; } -.ui-icon-seek-next { background-position: -32px -160px; } -.ui-icon-seek-prev { background-position: -48px -160px; } -.ui-icon-seek-end { background-position: -64px -160px; } -.ui-icon-seek-start { background-position: -80px -160px; } -/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ -.ui-icon-seek-first { background-position: -80px -160px; } -.ui-icon-stop { background-position: -96px -160px; } -.ui-icon-eject { background-position: -112px -160px; } -.ui-icon-volume-off { background-position: -128px -160px; } -.ui-icon-volume-on { background-position: -144px -160px; } -.ui-icon-power { background-position: 0 -176px; } -.ui-icon-signal-diag { background-position: -16px -176px; } -.ui-icon-signal { background-position: -32px -176px; } -.ui-icon-battery-0 { background-position: -48px -176px; } -.ui-icon-battery-1 { background-position: -64px -176px; } -.ui-icon-battery-2 { background-position: -80px -176px; } -.ui-icon-battery-3 { background-position: -96px -176px; } -.ui-icon-circle-plus { background-position: 0 -192px; } -.ui-icon-circle-minus { background-position: -16px -192px; } -.ui-icon-circle-close { background-position: -32px -192px; } -.ui-icon-circle-triangle-e { background-position: -48px -192px; } -.ui-icon-circle-triangle-s { background-position: -64px -192px; } -.ui-icon-circle-triangle-w { background-position: -80px -192px; } -.ui-icon-circle-triangle-n { background-position: -96px -192px; } -.ui-icon-circle-arrow-e { background-position: -112px -192px; } -.ui-icon-circle-arrow-s { background-position: -128px -192px; } -.ui-icon-circle-arrow-w { background-position: -144px -192px; } -.ui-icon-circle-arrow-n { background-position: -160px -192px; } -.ui-icon-circle-zoomin { background-position: -176px -192px; } -.ui-icon-circle-zoomout { background-position: -192px -192px; } -.ui-icon-circle-check { background-position: -208px -192px; } -.ui-icon-circlesmall-plus { background-position: 0 -208px; } -.ui-icon-circlesmall-minus { background-position: -16px -208px; } -.ui-icon-circlesmall-close { background-position: -32px -208px; } -.ui-icon-squaresmall-plus { background-position: -48px -208px; } -.ui-icon-squaresmall-minus { background-position: -64px -208px; } -.ui-icon-squaresmall-close { background-position: -80px -208px; } -.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } -.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } -.ui-icon-grip-solid-vertical { background-position: -32px -224px; } -.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } -.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } -.ui-icon-grip-diagonal-se { background-position: -80px -224px; } - - -/* Misc visuals -----------------------------------*/ - -/* Corner radius */ -.ui-corner-all, -.ui-corner-top, -.ui-corner-left, -.ui-corner-tl { - border-top-left-radius: 2px; -} -.ui-corner-all, -.ui-corner-top, -.ui-corner-right, -.ui-corner-tr { - border-top-right-radius: 2px; -} -.ui-corner-all, -.ui-corner-bottom, -.ui-corner-left, -.ui-corner-bl { - border-bottom-left-radius: 2px; -} -.ui-corner-all, -.ui-corner-bottom, -.ui-corner-right, -.ui-corner-br { - border-bottom-right-radius: 2px; -} - -/* Overlays */ -.ui-widget-overlay { - background: #aaaaaa; - opacity: .3; - filter: Alpha(Opacity=30); /* support: IE8 */ -} -.ui-widget-shadow { - margin: 0px 0 0 0px; - padding: 5px; - background: #666666; - opacity: .3; - filter: Alpha(Opacity=30); /* support: IE8 */ - border-radius: 8px; -} diff --git a/web/skins/classic/css/dark/views/control.css b/web/skins/classic/css/dark/views/control.css index af0eeccf2..31a937323 100644 --- a/web/skins/classic/css/dark/views/control.css +++ b/web/skins/classic/css/dark/views/control.css @@ -21,7 +21,7 @@ } .ptzControls input[type=image] { - border: 0px; + border: 0; } .ptzControls .controlsPanel .arrowControl { @@ -115,7 +115,7 @@ } .ptzControls .controlsPanel .pantiltPanel .pantiltControls .centerBtn { - background: url("../skins/classic/graphics/graphics/center.png") no-repeat 0 0; + background: url("../skins/classic/graphics/center.png") no-repeat 0 0; } .ptzControls .controlsPanel .pantiltPanel .pantiltControls .rightBtn { diff --git a/web/skins/classic/css/dark/views/frame.css b/web/skins/classic/css/dark/views/frame.css index c9cb1846c..947fee1bc 100644 --- a/web/skins/classic/css/dark/views/frame.css +++ b/web/skins/classic/css/dark/views/frame.css @@ -9,8 +9,3 @@ display: flex; justify-content: space-between; } - -#controls a { - width: 40px; - margin-left: -20px; -} diff --git a/web/skins/classic/css/dark/views/timeline.css.php b/web/skins/classic/css/dark/views/timeline.css.php index 85078d107..e69de29bb 100644 --- a/web/skins/classic/css/dark/views/timeline.css.php +++ b/web/skins/classic/css/dark/views/timeline.css.php @@ -1,77 +0,0 @@ -.chartSize { - width: px; - height: px; -} - -.graphSize { - width: px; - height: px; -} - -.graphHeight { - height: px; -} - -.graphWidth { - width: px; -} - -.imageSize { - width: px; - height: px; -} - -.imageHeight { - height: px; -} - -.activitySize { - width: px; - height: px; -} - -.eventsSize { - width: px; - height: px; -} - -.eventsHeight { - height: px; -} - -#chartPanel .eventsPos { - top: px; -} - -#chartPanel .activityPos { - top: px; -} - -#chartPanel .eventsPos { - top: px; -} - -.monitorColour { - background-color: ; -} - diff --git a/web/skins/classic/includes/control_functions.php b/web/skins/classic/includes/control_functions.php index 7d4b7950e..80cfe4aff 100644 --- a/web/skins/classic/includes/control_functions.php +++ b/web/skins/classic/includes/control_functions.php @@ -18,125 +18,22 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -function getControlCommands( $monitor ) { - $cmds = array(); - $cmds['Wake'] = "wake"; - $cmds['Sleep'] = "sleep"; - $cmds['Reset'] = "reset"; - - $cmds['PresetSet'] = "presetSet"; - $cmds['PresetGoto'] = "presetGoto"; - $cmds['PresetHome'] = "presetHome"; - - if ( $monitor->CanZoom() ) { - if ( $monitor->CanZoomCon() ) - $cmds['ZoomRoot'] = "zoomCon"; - elseif ( $monitor->CanZoomRel() ) - $cmds['ZoomRoot'] = "zoomRel"; - elseif ( $monitor->CanZoomAbs() ) - $cmds['ZoomRoot'] = "zoomAbs"; - $cmds['ZoomTele'] = $cmds['ZoomRoot']."Tele"; - $cmds['ZoomWide'] = $cmds['ZoomRoot']."Wide"; - $cmds['ZoomStop'] = "zoomStop"; - $cmds['ZoomAuto'] = "zoomAuto"; - $cmds['ZoomMan'] = "zoomMan"; - } - - if ( $monitor->CanFocus() ) { - if ( $monitor->CanFocusCon() ) - $cmds['FocusRoot'] = "focusCon"; - elseif ( $monitor->CanFocusRel() ) - $cmds['FocusRoot'] = "focusRel"; - elseif ( $monitor->CanFocusAbs() ) - $cmds['FocusRoot'] = "focusAbs"; - $cmds['FocusFar'] = $cmds['FocusRoot']."Far"; - $cmds['FocusNear'] = $cmds['FocusRoot']."Near"; - $cmds['FocusStop'] = "focusStop"; - $cmds['FocusAuto'] = "focusAuto"; - $cmds['FocusMan'] = "focusMan"; - } - - if ( $monitor->CanIris() ) { - if ( $monitor->CanIrisCon() ) - $cmds['IrisRoot'] = "irisCon"; - elseif ( $monitor->CanIrisRel() ) - $cmds['IrisRoot'] = "irisRel"; - elseif ( $monitor->CanIrisAbs() ) - $cmds['IrisRoot'] = "irisAbs"; - $cmds['IrisOpen'] = $cmds['IrisRoot']."Open"; - $cmds['IrisClose'] = $cmds['IrisRoot']."Close"; - $cmds['IrisStop'] = "irisStop"; - $cmds['IrisAuto'] = "irisAuto"; - $cmds['IrisMan'] = "irisMan"; - } - - if ( $monitor->CanWhite() ) { - if ( $monitor->CanWhiteCon() ) - $cmds['WhiteRoot'] = "whiteCon"; - elseif ( $monitor->CanWhiteRel() ) - $cmds['WhiteRoot'] = "whiteRel"; - elseif ( $monitor->CanWhiteAbs() ) - $cmds['WhiteRoot'] = "whiteAbs"; - $cmds['WhiteIn'] = $cmds['WhiteRoot']."In"; - $cmds['WhiteOut'] = $cmds['WhiteRoot']."Out"; - $cmds['WhiteAuto'] = "whiteAuto"; - $cmds['WhiteMan'] = "whiteMan"; - } - - if ( $monitor->CanGain() ) { - if ( $monitor->CanGainCon() ) - $cmds['GainRoot'] = "gainCon"; - elseif ( $monitor->CanGainRel() ) - $cmds['GainRoot'] = "gainRel"; - elseif ( $monitor->CanGainAbs() ) - $cmds['GainRoot'] = "gainAbs"; - $cmds['GainUp'] = $cmds['GainRoot']."Up"; - $cmds['GainDown'] = $cmds['GainRoot']."Down"; - $cmds['GainAuto'] = "gainAuto"; - $cmds['GainMan'] = "gainMan"; - } - - if ( $monitor->CanMove() ) { - if ( $monitor->CanMoveCon() ) { - $cmds['MoveRoot'] = "moveCon"; - $cmds['Center'] = "moveStop"; - } elseif ( $monitor->CanMoveRel() ) { - $cmds['MoveRoot'] = "moveRel"; - $cmds['Center'] = $cmds['PresetHome']; - } elseif ( $monitor->CanMoveAbs() ) { - $cmds['MoveRoot'] = "moveAbs"; - $cmds['Center'] = $cmds['PresetHome']; - } else { - $cmds['MoveRoot'] = ''; - } - - $cmds['MoveUp'] = $cmds['MoveRoot']."Up"; - $cmds['MoveDown'] = $cmds['MoveRoot']."Down"; - $cmds['MoveLeft'] = $cmds['MoveRoot']."Left"; - $cmds['MoveRight'] = $cmds['MoveRoot']."Right"; - $cmds['MoveUpLeft'] = $cmds['MoveRoot']."UpLeft"; - $cmds['MoveUpRight'] = $cmds['MoveRoot']."UpRight"; - $cmds['MoveDownLeft'] = $cmds['MoveRoot']."DownLeft"; - $cmds['MoveDownRight'] = $cmds['MoveRoot']."DownRight"; - } - return( $cmds ); -} - -function controlFocus( $monitor, $cmds ) { +function controlFocus($monitor, $cmds) { + $control = $monitor->Control(); ob_start(); ?>
-
-
CanFocusCon() ) { ?> onclick="controlCmd('')">
-
+ + +
CanAutoFocus() ) { + if ( $control->CanAutoFocus() ) { ?> - - + + @@ -145,22 +42,21 @@ function controlFocus( $monitor, $cmds ) { return ob_get_clean(); } -function controlZoom( $monitor, $cmds ) { - global $SLANG; - +function controlZoom($monitor, $cmds) { + $control = $monitor->Control(); ob_start(); ?>
-
-
CanZoomCon() ) { ?> onclick="controlCmd('')">
-
+ + +
CanAutoZoom() ) { + if ( $control->CanAutoZoom() ) { ?> - - + + @@ -168,22 +64,21 @@ function controlZoom( $monitor, $cmds ) { return ob_get_clean(); } -function controlIris( $monitor, $cmds ) { - global $SLANG; - +function controlIris($monitor, $cmds) { + $control = $monitor->Control(); ob_start(); ?>
-
-
CanIrisCon() ) { ?> onclick="controlCmd('')">
-
+ + +
CanAutoIris() ) { + if ( $control->CanAutoIris() ) { ?> - - + + @@ -192,22 +87,21 @@ function controlIris( $monitor, $cmds ) { return ob_get_clean(); } -function controlWhite( $monitor, $cmds ) { - global $SLANG; - +function controlWhite($monitor, $cmds) { + $control = $monitor->Control(); ob_start(); ?>
-
-
CanWhiteCon() ) { ?> onclick="controlCmd('')">
-
+ + +
CanAutoWhite() ) { + if ( $control->CanAutoWhite() ) { ?> - - + + @@ -216,43 +110,41 @@ function controlWhite( $monitor, $cmds ) { return ob_get_clean(); } -function controlPanTilt( $monitor, $cmds ) { - global $SLANG; - +function controlPanTilt($monitor, $cmds) { + $control = $monitor->Control(); ob_start(); ?>
CanPan(); - $hasTilt = $monitor->CanTilt(); - $hasDiag = $hasPan && $hasTilt && $monitor->CanMoveDiag(); + $hasPan = $control->CanPan(); + $hasTilt = $control->CanTilt(); + $hasDiag = $hasPan && $hasTilt && $control->CanMoveDiag(); ?> -
-
-
-
+ + + + -
+ -
+ -
-
-
-
+ + + +
Control(); // MAX_PRESETS IS PER LINE - define( "MAX_PRESETS", "12" ); + define('MAX_PRESETS', '12'); $sql = 'SELECT * FROM ControlPresets WHERE MonitorId = ?'; $labels = array(); @@ -260,7 +152,7 @@ function controlPresets( $monitor, $cmds ) { $labels[$row['Preset']] = $row['Label']; } - $presetBreak = (int)(($monitor->NumPresets()+1)/((int)(($monitor->NumPresets()-1)/MAX_PRESETS)+1)); + $presetBreak = (int)(($control->NumPresets()+1)/((int)(($control->NumPresets()-1)/MAX_PRESETS)+1)); ob_start(); ?> @@ -268,26 +160,23 @@ function controlPresets( $monitor, $cmds ) {
NumPresets(); $i++ ) { + for ( $i = 1; $i <= $control->NumPresets(); $i++ ) { ?> - " value="" onclick="controlCmd('');"/> +
HasHomePreset() ) { + if ( $control->HasHomePreset() ) { ?> - + CanSetPresets() ) { + if ( canEdit('Monitors') && $control->CanSetPresets() ) { ?> - + @@ -297,28 +186,32 @@ function controlPresets( $monitor, $cmds ) { return ob_get_clean(); } -function controlPower( $monitor, $cmds ) { - global $SLANG; - +function controlPower($monitor, $cmds) { + $control = $monitor->Control(); ob_start(); ?>
CanWake() ) { + if ( $control->CanWake() ) { ?> - + CanSleep() ) { + if ( $control->CanSleep() ) { ?> - + CanReset() ) { + if ( $control->CanReset() ) { ?> - + +CanReboot() ) { +?> + @@ -328,30 +221,33 @@ function controlPower( $monitor, $cmds ) { return ob_get_clean(); } -function ptzControls( $monitor ) { - $cmds = getControlCommands($monitor); +function ptzControls($monitor) { + $control = $monitor->Control(); + //ZM\Error("Control: " . print_r($control,true)); + $cmds = $control->commands(); + //ZM\Error("Cmds: " . print_r($cmds, true)); ob_start(); ?>
CanFocus() ) + if ( $control->CanFocus() ) echo controlFocus($monitor, $cmds); - if ( $monitor->CanZoom() ) + if ( $control->CanZoom() ) echo controlZoom($monitor, $cmds); - if ( $monitor->CanIris() ) + if ( $control->CanIris() ) echo controlIris($monitor, $cmds); - if ( $monitor->CanWhite() ) + if ( $control->CanWhite() ) echo controlWhite($monitor, $cmds); - if ( $monitor->CanMove() ) { + if ( $control->CanMove() ) { ?>
CanWake() || $monitor->CanSleep() || $monitor->CanReset() ) + if ( $control->CanWake() || $control->CanSleep() || $control->CanReset() ) echo controlPower($monitor, $cmds); - if ( $monitor->HasPresets() ) + if ( $control->HasPresets() ) echo controlPresets($monitor, $cmds); ?>
diff --git a/web/skins/classic/includes/export_functions.php b/web/skins/classic/includes/export_functions.php index 7750f165b..d43c697e3 100644 --- a/web/skins/classic/includes/export_functions.php +++ b/web/skins/classic/includes/export_functions.php @@ -18,21 +18,18 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -function exportHeader( $title ) -{ +function exportHeader($title) { ?> - - - - <?php echo $title ?> - - - + + - + }); + + Id() ); +function exportEventDetail($event, $exportFrames, $exportImages) { + ob_start(); + exportHeader(translate('Event').' '.$event->Id()); $otherlinks = ''; if( $exportFrames ) $otherlinks .= ''.translate('Frames').','; if( $exportImages ) $otherlinks .= ''.translate('Images').','; $otherlinks = substr($otherlinks,0,-1); - - ?>
@@ -122,39 +113,37 @@ function exportEventDetail( $event, $exportFrames, $exportImages ) - + - + - +
Id() ?>
Name()) ?>
MonitorName()) ?> (MonitorId() ?>)
Monitor()->Name()) ?> (MonitorId() ?>)
Cause()) ?>
Notes()) ?>
StartTime()) ) ?>
StartTime())) ?>
Length() ?>
Frames() ?>
AlarmFrames() ?>
TotScore() ?>
AvgScore() ?>
MaxScore() ?>
Archived()?translate('Yes'):translate('No') ?>
Archived()?'Yes':'No') ?>
Id() ) ); +function exportEventFrames($event, $exportDetail, $exportImages) { + $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() ); + ob_start(); + exportHeader(translate('Frames').' '.$event->Id()); $otherlinks = ''; - if( $exportDetail ) $otherlinks .= ''.translate('Event').','; - if( $exportImages ) $otherlinks .= ''.translate('Images').','; + if ( $exportDetail ) $otherlinks .= ''.translate('Event').','; + if ( $exportImages ) $otherlinks .= ''.translate('Images').','; $otherlinks = substr($otherlinks,0,-1); - ?>
@@ -168,56 +157,49 @@ function exportEventFrames( $event, $exportDetail, $exportImages ) Path(); - foreach ( $frames as $frame ) - { - $imageFile = sprintf( "%0".ZM_EVENT_IMAGE_DIGITS."d-capture.jpg", $frame['FrameId'] ); - $imagePath = $eventPath."/".$imageFile; - $analImage = preg_replace( "/capture/", "analyse", $imagePath ); - if ( file_exists( $analImage ) ) - { - $imageFile = preg_replace( "/capture/", "analyse", $imageFile ); - } + if ( count($frames) ) { + $eventPath = $event->Path(); + foreach ( $frames as $frame ) { + $imageFile = sprintf('%0'.ZM_EVENT_IMAGE_DIGITS.'d-capture.jpg', $frame['FrameId']); + $imagePath = $eventPath.'/'.$imageFile; + $analImage = preg_replace('/capture/', 'analyse', $imagePath); + if ( file_exists($analImage) ) { + $imageFile = preg_replace('/capture/', 'analyse', $imageFile); + } - $class = strtolower($frame['Type']); + $class = strtolower($frame['Type']); ?> - - + + - Frame <?php echo $frame['FrameId'] ?> + Frame <?php echo $frame['FrameId'] ?>
@@ -225,13 +207,12 @@ function exportEventFrames( $event, $exportDetail, $exportImages ) Id() ); +function exportEventImages($event, $exportDetail, $exportFrames, $myfilelist) { + ob_start(); + exportHeader(translate('Images').' '.$event->Id()); $otherlinks = ''; if( $exportDetail ) $otherlinks .= ''.translate('Event').','; @@ -245,40 +226,66 @@ function exportEventImages( $event, $exportDetail, $exportFrames, $myfilelist ) ?>

: Name()) ?> ()

+DefaultVideo() ) { + // videojs zoomrotate only when direct recording + $Zoom = 1; + $Rotation = 0; + $Monitor = $event->Monitor(); + if ( $Monitor->VideoWriter() == '2' ) { + # Passthrough + $Rotation = $event->Orientation(); + if ( in_array($event->Orientation(),array('ROTATE_90','ROTATE_270')) ) + $Zoom = $event->Height()/$event->Width(); + } +?> +
+ +
+ - +

- + + + + + +
-
-
 
+
+
 
 
-
-
+ onmousedown="slide(event,'horizontal', Width()-20)?>, 1, , ,0, 'imageslider_display_id');"> 
+
+
+
- - - + +
+SaveJPEGs() ) { +?> + + +<?php echo $Event->Id()?> +Id(); } ?> + +DefaultVideo() ) { + if ( ZM_WEB_LIST_THUMBS ) { +?> + + <?php echo $Event->Id()?> + + +
+

Master

$eids)); + + foreach ($events as $event) { //get monitor id and event id - $event = new Event( $eid ); $eventMonitorId[$eid] = $event->MonitorId(); $eventPath[$eid] = $event->Relative_Path(); } @@ -600,70 +630,59 @@ function exportEventImagesMaster( $eids ) { $monitorNames = array(); //* - if(!empty($monitors)) { - $tmp = dbFetchAll("SELECT Id,Name FROM Monitors WHERE Id IN (".implode(',', $monitors).") "); + if ( !empty($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)); - ?> - - -
-
-

All

- $eids
"; - } - ?> - -
- + + + + +
+
+

All

+
- "; - echo "

Monitor: " . $monitorNames[$monitor] . "

"; - foreach ($eids as $eid) { - if ($eventMonitorId[$eid] == $monitor) { - ?> -
- '; - } - ?> +"; + echo '

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

'; + foreach ( $events as $event ) { + if ( $event->MonitorId() == $monitor_id ) { + eventlist_html($event); + } # end if its the right monitor + } # end foreach event + echo ''; + } # end foreach monitor +?> +
+ +
-
- - - -
- - - - + + Path(); + $eventPath = $event->Path(); + $eventRelativePath = $event->Relative_Path(); $files = array(); if ( $dir = opendir($eventPath) ) { while ( ($file = readdir($dir)) !== false ) { @@ -754,29 +773,29 @@ function exportFileList( $eid, $exportDetail, $exportFrames, $exportImages, $exp if ( $exportDetail ) { $file = 'zmEventDetail.html'; - if ( !($fp = fopen( $eventPath.'/'.$file, 'w' )) ) { - Fatal( "Can't open event detail export file '$file'" ); + if ( !($fp = fopen($eventPath.'/'.$file, 'w')) ) { + ZM\Fatal("Can't open event detail export file '$file'"); } - fwrite( $fp, exportEventDetail( $event, $exportFrames, $exportImages ) ); - fclose( $fp ); - $exportFileList[$file] = $eventPath."/".$file; + fwrite($fp, exportEventDetail($event, $exportFrames, $exportImages)); + fclose($fp); + $exportFileList[$file] = $file; } if ( $exportFrames ) { $file = 'zmEventFrames.html'; - if ( !($fp = fopen( $eventPath.'/'.$file, 'w' )) ) { - Fatal( "Can't open event frames export file '$file'" ); + if ( !($fp = fopen($eventPath.'/'.$file, 'w')) ) { + ZM\Fatal("Can't open event frames export file '$file'"); } - fwrite( $fp, exportEventFrames( $event, $exportDetail, $exportImages ) ); - fclose( $fp ); - $exportFileList[$file] = $eventPath."/".$file; + fwrite($fp, exportEventFrames($event, $exportDetail, $exportImages)); + fclose($fp); + $exportFileList[$file] = $file; } + if ( $exportImages ) { $filesLeft = array(); $myfilelist = array(); foreach ( $files as $file ) { - if ( preg_match( '/-(?:capture|analyse).jpg$/', $file ) ) { - $exportFileList[$file] = $eventPath."/".$file; - $myfilelist[$file] = $eventPath."/".$file; + if ( preg_match('/-(?:capture|analyse).jpg$/', $file ) ) { + $myfilelist[$file] = $exportFileList[$file] = $file; } else { $filesLeft[$file] = $file; } @@ -786,113 +805,173 @@ function exportFileList( $eid, $exportDetail, $exportFrames, $exportImages, $exp // create an image slider if ( !empty($myfilelist) ) { $file = 'zmEventImages.html'; - if ( !($fp = fopen($eventPath.'/'.$file, 'w')) ) Fatal( "Can't open event images export file '$file'" ); - fwrite( $fp, exportEventImages( $event, $exportDetail, $exportFrames, $myfilelist ) ); - fclose( $fp ); - $exportFileList[$file] = $eventPath."/".$file; + 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] = $file; } } # end if exportImages if ( $exportVideo ) { $filesLeft = array(); foreach ( $files as $file ) { - if ( preg_match( '/\.(?:mpg|mpeg|mov|swf|mp4|mkv|avi|asf|3gp)$/', $file ) ) { - $exportFileList[$file] = $eventPath.'/'.$file; + if ( preg_match('/\.(?:mpg|mpeg|mov|swf|mp4|mkv|avi|asf|3gp)$/', $file) ) { + $exportFileList[$file] = $file; } else { $filesLeft[$file] = $file; } } $files = $filesLeft; - } # end if exportVideo + } # end if exportVideo if ( $exportMisc ) { foreach ( $files as $file ) { - $exportFileList[$file] = $eventPath.'/'.$file; + $exportFileList[$file] = $file; } $files = array(); } return array_values($exportFileList); -} +} # end exportFileList() -function exportEvents( $eids, $exportDetail, $exportFrames, $exportImages, $exportVideo, $exportMisc, $exportFormat, $exportStructure = false ) { +function exportEvents( + $eids, + $connkey, + $exportDetail, + $exportFrames, + $exportImages, + $exportVideo, + $exportMisc, + $exportFormat, + $exportCompressed, + $exportStructure = false +) { - if ( (!canView('Events')) || empty($eids) ) { + if ( !canView('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.'); return false; } + + if ( !($exportFormat == 'tar' or $exportFormat == 'zip') ) { + ZM\Error("None or invalid exportFormat specified $exportFormat."); + return false; + } + + # Ensure that we are going to be able to do this. + 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 ( ! ( mkdir($export_dir) or file_exists($export_dir) ) ) { + ZM\Fatal("Can't create exports dir at '$export_dir'"); + } else { + ZM\Logger::Debug("Successfully created dir '$export_dir'"); + } + chmod($export_dir, 0700); + if ( !chdir($export_dir) ) { + ZM\Fatal("Can't chdir to $export_dir"); + } + $export_root = 'zmExport'; $export_listFile = 'zmFileList.txt'; $exportFileList = array(); $html_eventMaster = ''; - if ( is_array($eids) ) { - foreach ( $eids as $eid ) { - $exportFileList = array_merge( $exportFileList, exportFileList( $eid , $exportDetail, $exportFrames, $exportImages, $exportVideo, $exportMisc ) ); - } - } else { - $eid = $eids; - $exportFileList = exportFileList( $eid, $exportDetail, $exportFrames, $exportImages, $exportVideo, $exportMisc ); + if ( !is_array($eids) ) { + $eids = array($eids); } - - // create an master image slider - if ( $exportImages ) { - 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) or file_exists($event_dir) ) ) { + ZM\Error("Can't mkdir $event_dir"); + } + $event_exportFileList = exportFileList($event, $exportDetail, $exportFrames, $exportImages, $exportVideo, $exportMisc); + ZM\Logger::Debug("File list for event $eid " . print_r($event_exportFileList, true)); + $exportFileList = array_merge($exportFileList,$event_exportFileList); + foreach ( $event_exportFileList as $file ) { + if ( preg_match('/\.html$/', $file) ) + continue; + #exec('cp -as '.$event->Path().'/../'.$file.' '.$export_dir.'/'.$file, $output, $return); + $cmd = 'cp -as '.$event->Path().'/'.$file.' '.$export_dir.'/'.$event->Id().'/'.$file. ' 2>&1'; + exec($cmd, $output, $return); + ZM\Logger::Debug($cmd.' return code: '.$return.' output: '.print_r($output,true)); + } + } # end foreach event + + // create an master image + if ( $exportImages ) { + if ( !symlink(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/js/jquery.js', $export_dir.'/jquery.js') ) + ZM\Error('Failed linking jquery.js'); + //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; + + 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'"); } - $monitorPath = ZM_DIR_EVENTS.'/'; - $html_eventMaster = 'zmEventImagesMaster_'.date('Ymd_His'). '.html'; - if ( !($fp = fopen( $monitorPath.'/'.$html_eventMaster, 'w' )) ) Fatal( "Can't open event images export file '$html_eventMaster'" ); - fwrite($fp, exportEventImagesMaster($eids)); - fclose($fp); - $exportFileList[] = $monitorPath.'/'.$html_eventMaster; } - if ( ! file_exists(ZM_DIR_EXPORTS) ) { - if ( ! mkdir(ZM_DIR_EXPORTS) ) { - Fatal("Can't create exports dir at '".ZM_DIR_EXPORTS."'"); - } - } - $listFile = ZM_DIR_EXPORTS.'/'.$export_listFile; + $listFile = $export_dir.'/'.$export_listFile; if ( !($fp = fopen($listFile, 'w')) ) { - Fatal( "Can't open event export list file '$listFile'" ); + ZM\Fatal("Can't open event export list file '$listFile'"); } foreach ( $exportFileList as $exportFile ) { - fwrite( $fp, "$exportFile\n" ); + $exportFile = 'zmExport'.$connkey.'/'.$exportFile; + fwrite($fp, "$exportFile\n"); } - fclose( $fp ); + fwrite($fp, "$listFile\n"); + fclose($fp); + + chdir(ZM_DIR_EXPORTS); $archive = ''; if ( $exportFormat == 'tar' ) { - $archive = ZM_DIR_EXPORTS.'/'.$export_root.'.tar.gz'; - @unlink( $archive ); - if ( $exportStructure == 'flat' ) { //strip file paths if we choose - $command = "nice -10 tar --create --gzip --file=".escapeshellarg($archive)." --files-from=".escapeshellarg($listFile)." --xform='s#^.+/##x'"; - } else { - $command = "nice -10 tar --create --gzip --file=".escapeshellarg($archive)." --files-from=".escapeshellarg($listFile); - } - exec( $command, $output, $status ); - if ( $status ) { - Error( "Command '$command' returned with status $status" ); - if ( $output[0] ) - Error( "First line of output is '".$output[0]."'" ); - return( false ); - } - } elseif ( $exportFormat == 'zip' ) { - $archive = ZM_DIR_EXPORTS.'/'.$export_root.'.zip'; - @unlink( $archive ); - if ($exportStructure == 'flat') { - $command = "cat ".escapeshellarg($listFile)." | nice -10 zip -q -j ".escapeshellarg($archive)." -@"; - } else { - $command = "cat ".escapeshellarg($listFile)." | nice -10 zip -q ".escapeshellarg($archive)." -@"; - } - //cat zmFileList.txt | zip -q zm_export.zip -@ - //-bash: zip: command not found + $archive = ZM_DIR_EXPORTS.'/'.$export_root.($connkey?'_'.$connkey:'').'.tar'; + $version = shell_exec('tar -v'); - exec( $command, $output, $status ); - if ( $status ) { - Error("Command '$command' returned with status $status"); - if ( $output[0] ) - Error("First line of output is '".$output[0]."'"); - return false; + $command = 'tar --create --dereference'; + if ( $exportCompressed ) { + $archive .= '.gz'; + $command .= ' --gzip'; + $exportFormat .= '.gz'; } + if ( $exportStructure == 'flat' ) { + if (preg_match("/BSD/i", $version)) { + $command .= " -s '#^.*/##'"; + } else { + $command .= " --xform='s#^.+/##x'"; + } + } + $command .= ' --file='.escapeshellarg($archive); + } elseif ( $exportFormat == 'zip' ) { + $archive = ZM_DIR_EXPORTS.'/'.$export_root.($connkey?'_'.$connkey:'').'.zip'; + $command = 'zip '; + $command .= ($exportStructure == 'flat' ? ' -j ' : ' -r ' ).escapeshellarg($archive); + $command .= $exportCompressed ? ' -9' : ' -0'; + } // if $exportFormat + + @unlink($archive); + $command .= ' zmExport_' . $connkey.'/'; + 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].'\''); + } + return false; } //clean up temporary files @@ -900,5 +979,5 @@ function exportEvents( $eids, $exportDetail, $exportFrames, $exportImages, $expo unlink($monitorPath.'/'.$html_eventMaster); } - return '?view=archive%26type='.$exportFormat; + return '?view=archive%26type='.$exportFormat.'%26connkey='.$connkey; } diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index 66243daf3..ceaee50d5 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -19,34 +19,35 @@ // -function xhtmlHeaders( $file, $title ) { +function xhtmlHeaders($file, $title) { global $css; global $skin; global $view; # This idea is that we always include the classic css files, # and then any different skin only needs to contain things that are different. - $baseCssPhpFile = getSkinFile( 'css/base/skin.css.php' ); + $baseCssPhpFile = getSkinFile('css/base/skin.css.php'); - $skinCssPhpFile = getSkinFile( 'css/'.$css.'/skin.css.php' ); + $skinCssPhpFile = getSkinFile('css/'.$css.'/skin.css.php'); - $skinJsFile = getSkinFile( 'js/skin.js' ); - $skinJsPhpFile = getSkinFile( 'js/skin.js.php' ); - $cssJsFile = getSkinFile( 'js/'.$css.'.js' ); + $skinJsPhpFile = getSkinFile('js/skin.js.php'); + $cssJsFile = getSkinFile('js/'.$css.'.js'); - $basename = basename( $file, '.php' ); + $basename = basename($file, '.php'); - $viewCssPhpFile = getSkinFile( '/css/'.$css.'/views/'.$basename.'.css.php' ); - $viewJsFile = getSkinFile( 'views/js/'.$basename.'.js' ); - $viewJsPhpFile = getSkinFile( 'views/js/'.$basename.'.js.php' ); + $baseViewCssPhpFile = getSkinFile('/css/base/views/'.$basename.'.css.php'); + $viewCssPhpFile = getSkinFile('/css/'.$css.'/views/'.$basename.'.css.php'); + $viewJsFile = getSkinFile('views/js/'.$basename.'.js'); + $viewJsPhpFile = getSkinFile('views/js/'.$basename.'.js.php'); - extract( $GLOBALS, EXTR_OVERWRITE ); - function output_link_if_exists( $files ) { + extract($GLOBALS, EXTR_OVERWRITE); + + function output_link_if_exists($files) { global $skin; $html = array(); foreach ( $files as $file ) { - if ( getSkinFile( $file ) ) { - $html[] = ''; + if ( getSkinFile($file) ) { + $html[] = ''; } } return implode("\n", $html); @@ -57,7 +58,8 @@ function xhtmlHeaders( $file, $title ) { - <?php echo ZM_WEB_TITLE_PREFIX ?> - <?php echo validHtmlStr($title) ?> + + <?php echo validHtmlStr(ZM_WEB_TITLE_PREFIX); ?> - <?php echo validHtmlStr($title) ?> - + + + @@ -119,16 +127,25 @@ echo output_link_if_exists( array( - - + @@ -147,27 +164,19 @@ echo output_link_if_exists( array( - - - - + + - + + + + - + @@ -192,7 +209,27 @@ echo output_link_if_exists( array( + +'; + global $error_message; + if ( $error_message ) { + echo '
'.$error_message.'
'; + } +} // end function getBodyTopHTML + function getNavBarHTML($reload = null) { + # Provide a facility to turn off the headers if you put navbar=0 into the url + if ( isset($_REQUEST['navbar']) and $_REQUEST['navbar']=='0' ) + return ''; $versionClass = (ZM_DYN_DB_VERSION&&(ZM_DYN_DB_VERSION!=ZM_VERSION))?'errorText':''; global $running; @@ -206,170 +243,212 @@ function getNavBarHTML($reload = null) { if (!$sortQuery) { parseSort(); } - if (!$filterQuery) { - parseFilter( $_REQUEST['filter'] ); + if ( (!$filterQuery) and isset($_REQUEST['filter']) ) { + parseFilter($_REQUEST['filter']); $filterQuery = $_REQUEST['filter']['query']; } - if ($reload === null) { + if ( $reload === null ) { ob_start(); if ( $running == null ) $running = daemonCheck(); - $status = $running?translate('Running'):translate('Stopped'); + if ( $running ) { + $state = dbFetchOne('SELECT Name FROM States WHERE isActive=1', 'Name'); + if ( $state == 'default' ) + $state = ''; + } + $status = $running ? ($state ? $state : translate('Running')) : translate('Stopped'); ?> - + - " + item.group_label + "" + item.html; + return "" + (this.escape_html(item.group_label)) + "" + item.html; } else { return item.html; } @@ -267,7 +267,9 @@ This file is generated by `grunt build`, do not edit it by hand. } option_el = document.createElement("li"); option_el.className = classes.join(" "); - option_el.style.cssText = option.style; + if (option.style) { + option_el.style.cssText = option.style; + } option_el.setAttribute("data-option-array-index", option.array_index); option_el.innerHTML = option.highlighted_html || option.html; if (option.title) { @@ -341,7 +343,7 @@ This file is generated by `grunt build`, do not edit it by hand. } }; - AbstractChosen.prototype.winnow_results = function() { + AbstractChosen.prototype.winnow_results = function(options) { var escapedQuery, fix, i, len, option, prefix, query, ref, regex, results, results_group, search_match, startpos, suffix, text; this.no_results_clear(); results = 0; @@ -397,7 +399,9 @@ This file is generated by `grunt build`, do not edit it by hand. return this.no_results(query); } else { this.update_results_content(this.results_option_build()); - return this.winnow_results_set_highlight(); + if (!(options != null ? options.skip_highlight : void 0)) { + return this.winnow_results_set_highlight(); + } } }; @@ -540,7 +544,7 @@ This file is generated by `grunt build`, do not edit it by hand. if (this.options.width != null) { return this.options.width; } else { - return this.form_field.offsetWidth + "px"; + return this.form_field.offsetWidth+15 + "px"; } }; @@ -941,7 +945,7 @@ This file is generated by `grunt build`, do not edit it by hand. this.results_data = SelectParser.select_to_array(this.form_field); if (this.is_multiple) { this.search_choices.find("li.search-choice").remove(); - } else if (!this.is_multiple) { + } else { this.single_set_selected_text(); if (this.disable_search || this.form_field.options.length <= this.disable_search_threshold) { this.search_field[0].readOnly = true; @@ -1154,14 +1158,20 @@ This file is generated by `grunt build`, do not edit it by hand. item.selected = true; this.form_field.options[item.options_index].selected = true; this.selected_option_count = null; - this.search_field.val(""); if (this.is_multiple) { this.choice_build(item); } else { this.single_set_selected_text(this.choice_label(item)); } if (this.is_multiple && (!this.hide_results_on_select || (evt.metaKey || evt.ctrlKey))) { - this.winnow_results(); + if (evt.metaKey || evt.ctrlKey) { + this.winnow_results({ + skip_highlight: true + }); + } else { + this.search_field.val(""); + this.winnow_results(); + } } else { this.results_hide(); this.show_search_field_default(); diff --git a/web/skins/classic/js/chosen/chosen.jquery.min.js b/web/skins/classic/js/chosen/chosen.jquery.min.js index d67677fbd..4ad164751 100644 --- a/web/skins/classic/js/chosen/chosen.jquery.min.js +++ b/web/skins/classic/js/chosen/chosen.jquery.min.js @@ -1,3 +1,3 @@ -/* Chosen v1.8.2 | (c) 2011-2017 by Harvest | MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md */ +/* Chosen v1.8.7 | (c) 2011-2018 by Harvest | MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md */ -(function(){var t,e,s,i,n=function(t,e){return function(){return t.apply(e,arguments)}},r=function(t,e){function s(){this.constructor=t}for(var i in e)o.call(e,i)&&(t[i]=e[i]);return s.prototype=e.prototype,t.prototype=new s,t.__super__=e.prototype,t},o={}.hasOwnProperty;(i=function(){function t(){this.options_index=0,this.parsed=[]}return t.prototype.add_node=function(t){return"OPTGROUP"===t.nodeName.toUpperCase()?this.add_group(t):this.add_option(t)},t.prototype.add_group=function(t){var e,s,i,n,r,o;for(e=this.parsed.length,this.parsed.push({array_index:e,group:!0,label:t.label,title:t.title?t.title:void 0,children:0,disabled:t.disabled,classes:t.className}),o=[],s=0,i=(r=t.childNodes).length;s"+t.group_label+""+t.html:t.html},t.prototype.mouse_enter=function(){return this.mouse_on_container=!0},t.prototype.mouse_leave=function(){return this.mouse_on_container=!1},t.prototype.input_focus=function(t){if(this.is_multiple){if(!this.active_field)return setTimeout(function(t){return function(){return t.container_mousedown()}}(this),50)}else if(!this.active_field)return this.activate_field()},t.prototype.input_blur=function(t){if(!this.mouse_on_container)return this.active_field=!1,setTimeout(function(t){return function(){return t.blur_test()}}(this),100)},t.prototype.label_click_handler=function(t){return this.is_multiple?this.container_mousedown(t):this.activate_field()},t.prototype.results_option_build=function(t){var e,s,i,n,r,o,h;for(e="",h=0,n=0,r=(o=this.results_data).length;n=this.max_shown_results));n++);return e},t.prototype.result_add_option=function(t){var e,s;return t.search_match&&this.include_option_in_results(t)?(e=[],t.disabled||t.selected&&this.is_multiple||e.push("active-result"),!t.disabled||t.selected&&this.is_multiple||e.push("disabled-result"),t.selected&&e.push("result-selected"),null!=t.group_array_index&&e.push("group-option"),""!==t.classes&&e.push(t.classes),s=document.createElement("li"),s.className=e.join(" "),s.style.cssText=t.style,s.setAttribute("data-option-array-index",t.array_index),s.innerHTML=t.highlighted_html||t.html,t.title&&(s.title=t.title),this.outerHTML(s)):""},t.prototype.result_add_group=function(t){var e,s;return(t.search_match||t.group_match)&&t.active_options>0?((e=[]).push("group-result"),t.classes&&e.push(t.classes),s=document.createElement("li"),s.className=e.join(" "),s.innerHTML=t.highlighted_html||this.escape_html(t.label),t.title&&(s.title=t.title),this.outerHTML(s)):""},t.prototype.results_update_field=function(){if(this.set_default_text(),this.is_multiple||this.results_reset_cleanup(),this.result_clear_highlight(),this.results_build(),this.results_showing)return this.winnow_results()},t.prototype.reset_single_select_options=function(){var t,e,s,i,n;for(n=[],t=0,e=(s=this.results_data).length;t"+this.escape_html(e)+""+this.escape_html(d)),null!=_&&(_.group_match=!0)):null!=n.group_array_index&&this.results_data[n.group_array_index].search_match&&(n.search_match=!0)));return this.result_clear_highlight(),c<1&&o.length?(this.update_results_content(""),this.no_results(o)):(this.update_results_content(this.results_option_build()),this.winnow_results_set_highlight())},t.prototype.get_search_regex=function(t){var e,s;return s=this.search_contains?t:"(^|\\s|\\b)"+t+"[^\\s]*",this.enable_split_word_search||this.search_contains||(s="^"+s),e=this.case_sensitive_search?"":"i",new RegExp(s,e)},t.prototype.search_string_match=function(t,e){var s;return s=e.exec(t),!this.search_contains&&(null!=s?s[1]:void 0)&&(s.index+=1),s},t.prototype.choices_count=function(){var t,e,s;if(null!=this.selected_option_count)return this.selected_option_count;for(this.selected_option_count=0,t=0,e=(s=this.form_field.options).length;t0?this.keydown_backstroke():this.pending_backstroke||(this.result_clear_highlight(),this.results_search());break;case 13:t.preventDefault(),this.results_showing&&this.result_select(t);break;case 27:this.results_showing&&this.results_hide();break;case 9:case 16:case 17:case 18:case 38:case 40:case 91:break;default:this.results_search()}},t.prototype.clipboard_event_checker=function(t){if(!this.is_disabled)return setTimeout(function(t){return function(){return t.results_search()}}(this),50)},t.prototype.container_width=function(){return null!=this.options.width?this.options.width:this.form_field.offsetWidth+"px"},t.prototype.include_option_in_results=function(t){return!(this.is_multiple&&!this.display_selected_options&&t.selected)&&(!(!this.display_disabled_options&&t.disabled)&&!t.empty)},t.prototype.search_results_touchstart=function(t){return this.touch_started=!0,this.search_results_mouseover(t)},t.prototype.search_results_touchmove=function(t){return this.touch_started=!1,this.search_results_mouseout(t)},t.prototype.search_results_touchend=function(t){if(this.touch_started)return this.search_results_mouseup(t)},t.prototype.outerHTML=function(t){var e;return t.outerHTML?t.outerHTML:((e=document.createElement("div")).appendChild(t),e.innerHTML)},t.prototype.get_single_html=function(){return'\n '+this.default_text+'\n
\n
\n
\n \n
    \n
    '},t.prototype.get_multi_html=function(){return'
      \n
    • \n \n
    • \n
    \n
    \n
      \n
      '},t.prototype.get_no_results_html=function(t){return'
    • \n '+this.results_none_found+" "+this.escape_html(t)+"\n
    • "},t.browser_is_supported=function(){return"Microsoft Internet Explorer"===window.navigator.appName?document.documentMode>=8:!(/iP(od|hone)/i.test(window.navigator.userAgent)||/IEMobile/i.test(window.navigator.userAgent)||/Windows Phone/i.test(window.navigator.userAgent)||/BlackBerry/i.test(window.navigator.userAgent)||/BB10/i.test(window.navigator.userAgent)||/Android.*Mobile/i.test(window.navigator.userAgent))},t.default_multiple_text="Select Some Options",t.default_single_text="Select an Option",t.default_no_result_text="No results match",t}(),(t=jQuery).fn.extend({chosen:function(i){return e.browser_is_supported()?this.each(function(e){var n,r;r=(n=t(this)).data("chosen"),"destroy"!==i?r instanceof s||n.data("chosen",new s(this,i)):r instanceof s&&r.destroy()}):this}}),s=function(s){function n(){return n.__super__.constructor.apply(this,arguments)}return r(n,e),n.prototype.setup=function(){return this.form_field_jq=t(this.form_field),this.current_selectedIndex=this.form_field.selectedIndex},n.prototype.set_up_html=function(){var e,s;return(e=["chosen-container"]).push("chosen-container-"+(this.is_multiple?"multi":"single")),this.inherit_select_classes&&this.form_field.className&&e.push(this.form_field.className),this.is_rtl&&e.push("chosen-rtl"),s={"class":e.join(" "),title:this.form_field.title},this.form_field.id.length&&(s.id=this.form_field.id.replace(/[^\w]/g,"_")+"_chosen"),this.container=t("
      ",s),this.container.width(this.container_width()),this.is_multiple?this.container.html(this.get_multi_html()):this.container.html(this.get_single_html()),this.form_field_jq.hide().after(this.container),this.dropdown=this.container.find("div.chosen-drop").first(),this.search_field=this.container.find("input").first(),this.search_results=this.container.find("ul.chosen-results").first(),this.search_field_scale(),this.search_no_results=this.container.find("li.no-results").first(),this.is_multiple?(this.search_choices=this.container.find("ul.chosen-choices").first(),this.search_container=this.container.find("li.search-field").first()):(this.search_container=this.container.find("div.chosen-search").first(),this.selected_item=this.container.find(".chosen-single").first()),this.results_build(),this.set_tab_index(),this.set_label_behavior()},n.prototype.on_ready=function(){return this.form_field_jq.trigger("chosen:ready",{chosen:this})},n.prototype.register_observers=function(){return this.container.on("touchstart.chosen",function(t){return function(e){t.container_mousedown(e)}}(this)),this.container.on("touchend.chosen",function(t){return function(e){t.container_mouseup(e)}}(this)),this.container.on("mousedown.chosen",function(t){return function(e){t.container_mousedown(e)}}(this)),this.container.on("mouseup.chosen",function(t){return function(e){t.container_mouseup(e)}}(this)),this.container.on("mouseenter.chosen",function(t){return function(e){t.mouse_enter(e)}}(this)),this.container.on("mouseleave.chosen",function(t){return function(e){t.mouse_leave(e)}}(this)),this.search_results.on("mouseup.chosen",function(t){return function(e){t.search_results_mouseup(e)}}(this)),this.search_results.on("mouseover.chosen",function(t){return function(e){t.search_results_mouseover(e)}}(this)),this.search_results.on("mouseout.chosen",function(t){return function(e){t.search_results_mouseout(e)}}(this)),this.search_results.on("mousewheel.chosen DOMMouseScroll.chosen",function(t){return function(e){t.search_results_mousewheel(e)}}(this)),this.search_results.on("touchstart.chosen",function(t){return function(e){t.search_results_touchstart(e)}}(this)),this.search_results.on("touchmove.chosen",function(t){return function(e){t.search_results_touchmove(e)}}(this)),this.search_results.on("touchend.chosen",function(t){return function(e){t.search_results_touchend(e)}}(this)),this.form_field_jq.on("chosen:updated.chosen",function(t){return function(e){t.results_update_field(e)}}(this)),this.form_field_jq.on("chosen:activate.chosen",function(t){return function(e){t.activate_field(e)}}(this)),this.form_field_jq.on("chosen:open.chosen",function(t){return function(e){t.container_mousedown(e)}}(this)),this.form_field_jq.on("chosen:close.chosen",function(t){return function(e){t.close_field(e)}}(this)),this.search_field.on("blur.chosen",function(t){return function(e){t.input_blur(e)}}(this)),this.search_field.on("keyup.chosen",function(t){return function(e){t.keyup_checker(e)}}(this)),this.search_field.on("keydown.chosen",function(t){return function(e){t.keydown_checker(e)}}(this)),this.search_field.on("focus.chosen",function(t){return function(e){t.input_focus(e)}}(this)),this.search_field.on("cut.chosen",function(t){return function(e){t.clipboard_event_checker(e)}}(this)),this.search_field.on("paste.chosen",function(t){return function(e){t.clipboard_event_checker(e)}}(this)),this.is_multiple?this.search_choices.on("click.chosen",function(t){return function(e){t.choices_click(e)}}(this)):this.container.on("click.chosen",function(t){t.preventDefault()})},n.prototype.destroy=function(){return t(this.container[0].ownerDocument).off("click.chosen",this.click_test_action),this.form_field_label.length>0&&this.form_field_label.off("click.chosen"),this.search_field[0].tabIndex&&(this.form_field_jq[0].tabIndex=this.search_field[0].tabIndex),this.container.remove(),this.form_field_jq.removeData("chosen"),this.form_field_jq.show()},n.prototype.search_field_disabled=function(){return this.is_disabled=this.form_field.disabled||this.form_field_jq.parents("fieldset").is(":disabled"),this.container.toggleClass("chosen-disabled",this.is_disabled),this.search_field[0].disabled=this.is_disabled,this.is_multiple||this.selected_item.off("focus.chosen",this.activate_field),this.is_disabled?this.close_field():this.is_multiple?void 0:this.selected_item.on("focus.chosen",this.activate_field)},n.prototype.container_mousedown=function(e){var s;if(!this.is_disabled)return!e||"mousedown"!==(s=e.type)&&"touchstart"!==s||this.results_showing||e.preventDefault(),null!=e&&t(e.target).hasClass("search-choice-close")?void 0:(this.active_field?this.is_multiple||!e||t(e.target)[0]!==this.selected_item[0]&&!t(e.target).parents("a.chosen-single").length||(e.preventDefault(),this.results_toggle()):(this.is_multiple&&this.search_field.val(""),t(this.container[0].ownerDocument).on("click.chosen",this.click_test_action),this.results_show()),this.activate_field())},n.prototype.container_mouseup=function(t){if("ABBR"===t.target.nodeName&&!this.is_disabled)return this.results_reset(t)},n.prototype.search_results_mousewheel=function(t){var e;if(t.originalEvent&&(e=t.originalEvent.deltaY||-t.originalEvent.wheelDelta||t.originalEvent.detail),null!=e)return t.preventDefault(),"DOMMouseScroll"===t.type&&(e*=40),this.search_results.scrollTop(e+this.search_results.scrollTop())},n.prototype.blur_test=function(t){if(!this.active_field&&this.container.hasClass("chosen-container-active"))return this.close_field()},n.prototype.close_field=function(){return t(this.container[0].ownerDocument).off("click.chosen",this.click_test_action),this.active_field=!1,this.results_hide(),this.container.removeClass("chosen-container-active"),this.clear_backstroke(),this.show_search_field_default(),this.search_field_scale(),this.search_field.blur()},n.prototype.activate_field=function(){if(!this.is_disabled)return this.container.addClass("chosen-container-active"),this.active_field=!0,this.search_field.val(this.search_field.val()),this.search_field.focus()},n.prototype.test_active_click=function(e){var s;return(s=t(e.target).closest(".chosen-container")).length&&this.container[0]===s[0]?this.active_field=!0:this.close_field()},n.prototype.results_build=function(){return this.parsing=!0,this.selected_option_count=null,this.results_data=i.select_to_array(this.form_field),this.is_multiple?this.search_choices.find("li.search-choice").remove():this.is_multiple||(this.single_set_selected_text(),this.disable_search||this.form_field.options.length<=this.disable_search_threshold?(this.search_field[0].readOnly=!0,this.container.addClass("chosen-container-single-nosearch")):(this.search_field[0].readOnly=!1,this.container.removeClass("chosen-container-single-nosearch"))),this.update_results_content(this.results_option_build({first:!0})),this.search_field_disabled(),this.show_search_field_default(),this.search_field_scale(),this.parsing=!1},n.prototype.result_do_highlight=function(t){var e,s,i,n,r;if(t.length){if(this.result_clear_highlight(),this.result_highlight=t,this.result_highlight.addClass("highlighted"),i=parseInt(this.search_results.css("maxHeight"),10),r=this.search_results.scrollTop(),n=i+r,s=this.result_highlight.position().top+this.search_results.scrollTop(),(e=s+this.result_highlight.outerHeight())>=n)return this.search_results.scrollTop(e-i>0?e-i:0);if(s0)return this.form_field_label.on("click.chosen",this.label_click_handler)},n.prototype.show_search_field_default=function(){return this.is_multiple&&this.choices_count()<1&&!this.active_field?(this.search_field.val(this.default_text),this.search_field.addClass("default")):(this.search_field.val(""),this.search_field.removeClass("default"))},n.prototype.search_results_mouseup=function(e){var s;if((s=t(e.target).hasClass("active-result")?t(e.target):t(e.target).parents(".active-result").first()).length)return this.result_highlight=s,this.result_select(e),this.search_field.focus()},n.prototype.search_results_mouseover=function(e){var s;if(s=t(e.target).hasClass("active-result")?t(e.target):t(e.target).parents(".active-result").first())return this.result_do_highlight(s)},n.prototype.search_results_mouseout=function(e){if(t(e.target).hasClass("active-result")||t(e.target).parents(".active-result").first())return this.result_clear_highlight()},n.prototype.choice_build=function(e){var s,i;return s=t("
    • ",{"class":"search-choice"}).html(""+this.choice_label(e)+""),e.disabled?s.addClass("search-choice-disabled"):((i=t("",{"class":"search-choice-close","data-option-array-index":e.array_index})).on("click.chosen",function(t){return function(e){return t.choice_destroy_link_click(e)}}(this)),s.append(i)),this.search_container.before(s)},n.prototype.choice_destroy_link_click=function(e){if(e.preventDefault(),e.stopPropagation(),!this.is_disabled)return this.choice_destroy(t(e.target))},n.prototype.choice_destroy=function(t){if(this.result_deselect(t[0].getAttribute("data-option-array-index")))return this.active_field?this.search_field.focus():this.show_search_field_default(),this.is_multiple&&this.choices_count()>0&&this.get_search_field_value().length<1&&this.results_hide(),t.parents("li").first().remove(),this.search_field_scale()},n.prototype.results_reset=function(){if(this.reset_single_select_options(),this.form_field.options[0].selected=!0,this.single_set_selected_text(),this.show_search_field_default(),this.results_reset_cleanup(),this.trigger_form_field_change(),this.active_field)return this.results_hide()},n.prototype.results_reset_cleanup=function(){return this.current_selectedIndex=this.form_field.selectedIndex,this.selected_item.find("abbr").remove()},n.prototype.result_select=function(t){var e,s;if(this.result_highlight)return e=this.result_highlight,this.result_clear_highlight(),this.is_multiple&&this.max_selected_options<=this.choices_count()?(this.form_field_jq.trigger("chosen:maxselected",{chosen:this}),!1):(this.is_multiple?e.removeClass("active-result"):this.reset_single_select_options(),e.addClass("result-selected"),s=this.results_data[e[0].getAttribute("data-option-array-index")],s.selected=!0,this.form_field.options[s.options_index].selected=!0,this.selected_option_count=null,this.search_field.val(""),this.is_multiple?this.choice_build(s):this.single_set_selected_text(this.choice_label(s)),this.is_multiple&&(!this.hide_results_on_select||t.metaKey||t.ctrlKey)?this.winnow_results():(this.results_hide(),this.show_search_field_default()),(this.is_multiple||this.form_field.selectedIndex!==this.current_selectedIndex)&&this.trigger_form_field_change({selected:this.form_field.options[s.options_index].value}),this.current_selectedIndex=this.form_field.selectedIndex,t.preventDefault(),this.search_field_scale())},n.prototype.single_set_selected_text=function(t){return null==t&&(t=this.default_text),t===this.default_text?this.selected_item.addClass("chosen-default"):(this.single_deselect_control_build(),this.selected_item.removeClass("chosen-default")),this.selected_item.find("span").html(t)},n.prototype.result_deselect=function(t){var e;return e=this.results_data[t],!this.form_field.options[e.options_index].disabled&&(e.selected=!1,this.form_field.options[e.options_index].selected=!1,this.selected_option_count=null,this.result_clear_highlight(),this.results_showing&&this.winnow_results(),this.trigger_form_field_change({deselected:this.form_field.options[e.options_index].value}),this.search_field_scale(),!0)},n.prototype.single_deselect_control_build=function(){if(this.allow_single_deselect)return this.selected_item.find("abbr").length||this.selected_item.find("span").first().after(''),this.selected_item.addClass("chosen-single-with-deselect")},n.prototype.get_search_field_value=function(){return this.search_field.val()},n.prototype.get_search_text=function(){return t.trim(this.get_search_field_value())},n.prototype.escape_html=function(e){return t("
      ").text(e).html()},n.prototype.winnow_results_set_highlight=function(){var t,e;if(e=this.is_multiple?[]:this.search_results.find(".result-selected.active-result"),null!=(t=e.length?e.first():this.search_results.find(".active-result").first()))return this.result_do_highlight(t)},n.prototype.no_results=function(t){var e;return e=this.get_no_results_html(t),this.search_results.append(e),this.form_field_jq.trigger("chosen:no_results",{chosen:this})},n.prototype.no_results_clear=function(){return this.search_results.find(".no-results").remove()},n.prototype.keydown_arrow=function(){var t;return this.results_showing&&this.result_highlight?(t=this.result_highlight.nextAll("li.active-result").first())?this.result_do_highlight(t):void 0:this.results_show()},n.prototype.keyup_arrow=function(){var t;return this.results_showing||this.is_multiple?this.result_highlight?(t=this.result_highlight.prevAll("li.active-result")).length?this.result_do_highlight(t.first()):(this.choices_count()>0&&this.results_hide(),this.result_clear_highlight()):void 0:this.results_show()},n.prototype.keydown_backstroke=function(){var t;return this.pending_backstroke?(this.choice_destroy(this.pending_backstroke.find("a").first()),this.clear_backstroke()):(t=this.search_container.siblings("li.search-choice").last()).length&&!t.hasClass("search-choice-disabled")?(this.pending_backstroke=t,this.single_backstroke_delete?this.keydown_backstroke():this.pending_backstroke.addClass("search-choice-focus")):void 0},n.prototype.clear_backstroke=function(){return this.pending_backstroke&&this.pending_backstroke.removeClass("search-choice-focus"),this.pending_backstroke=null},n.prototype.search_field_scale=function(){var e,s,i,n,r,o,h;if(this.is_multiple){for(r={position:"absolute",left:"-1000px",top:"-1000px",display:"none",whiteSpace:"pre"},s=0,i=(o=["fontSize","fontStyle","fontWeight","fontFamily","lineHeight","textTransform","letterSpacing"]).length;s").css(r)).text(this.get_search_field_value()),t("body").append(e),h=e.width()+25,e.remove(),this.container.is(":visible")&&(h=Math.min(this.container.outerWidth()-10,h)),this.search_field.width(h)}},n.prototype.trigger_form_field_change=function(t){return this.form_field_jq.trigger("input",t),this.form_field_jq.trigger("change",t)},n}()}).call(this); \ No newline at end of file +(function(){var t,e,s,i,n=function(t,e){return function(){return t.apply(e,arguments)}},r=function(t,e){function s(){this.constructor=t}for(var i in e)o.call(e,i)&&(t[i]=e[i]);return s.prototype=e.prototype,t.prototype=new s,t.__super__=e.prototype,t},o={}.hasOwnProperty;(i=function(){function t(){this.options_index=0,this.parsed=[]}return t.prototype.add_node=function(t){return"OPTGROUP"===t.nodeName.toUpperCase()?this.add_group(t):this.add_option(t)},t.prototype.add_group=function(t){var e,s,i,n,r,o;for(e=this.parsed.length,this.parsed.push({array_index:e,group:!0,label:t.label,title:t.title?t.title:void 0,children:0,disabled:t.disabled,classes:t.className}),o=[],s=0,i=(r=t.childNodes).length;s"+this.escape_html(t.group_label)+""+t.html:t.html},t.prototype.mouse_enter=function(){return this.mouse_on_container=!0},t.prototype.mouse_leave=function(){return this.mouse_on_container=!1},t.prototype.input_focus=function(t){if(this.is_multiple){if(!this.active_field)return setTimeout(function(t){return function(){return t.container_mousedown()}}(this),50)}else if(!this.active_field)return this.activate_field()},t.prototype.input_blur=function(t){if(!this.mouse_on_container)return this.active_field=!1,setTimeout(function(t){return function(){return t.blur_test()}}(this),100)},t.prototype.label_click_handler=function(t){return this.is_multiple?this.container_mousedown(t):this.activate_field()},t.prototype.results_option_build=function(t){var e,s,i,n,r,o,h;for(e="",h=0,n=0,r=(o=this.results_data).length;n=this.max_shown_results));n++);return e},t.prototype.result_add_option=function(t){var e,s;return t.search_match&&this.include_option_in_results(t)?(e=[],t.disabled||t.selected&&this.is_multiple||e.push("active-result"),!t.disabled||t.selected&&this.is_multiple||e.push("disabled-result"),t.selected&&e.push("result-selected"),null!=t.group_array_index&&e.push("group-option"),""!==t.classes&&e.push(t.classes),s=document.createElement("li"),s.className=e.join(" "),t.style&&(s.style.cssText=t.style),s.setAttribute("data-option-array-index",t.array_index),s.innerHTML=t.highlighted_html||t.html,t.title&&(s.title=t.title),this.outerHTML(s)):""},t.prototype.result_add_group=function(t){var e,s;return(t.search_match||t.group_match)&&t.active_options>0?((e=[]).push("group-result"),t.classes&&e.push(t.classes),s=document.createElement("li"),s.className=e.join(" "),s.innerHTML=t.highlighted_html||this.escape_html(t.label),t.title&&(s.title=t.title),this.outerHTML(s)):""},t.prototype.results_update_field=function(){if(this.set_default_text(),this.is_multiple||this.results_reset_cleanup(),this.result_clear_highlight(),this.results_build(),this.results_showing)return this.winnow_results()},t.prototype.reset_single_select_options=function(){var t,e,s,i,n;for(n=[],t=0,e=(s=this.results_data).length;t"+this.escape_html(s)+""+this.escape_html(p)),null!=a&&(a.group_match=!0)):null!=r.group_array_index&&this.results_data[r.group_array_index].search_match&&(r.search_match=!0)));return this.result_clear_highlight(),_<1&&h.length?(this.update_results_content(""),this.no_results(h)):(this.update_results_content(this.results_option_build()),(null!=t?t.skip_highlight:void 0)?void 0:this.winnow_results_set_highlight())},t.prototype.get_search_regex=function(t){var e,s;return s=this.search_contains?t:"(^|\\s|\\b)"+t+"[^\\s]*",this.enable_split_word_search||this.search_contains||(s="^"+s),e=this.case_sensitive_search?"":"i",new RegExp(s,e)},t.prototype.search_string_match=function(t,e){var s;return s=e.exec(t),!this.search_contains&&(null!=s?s[1]:void 0)&&(s.index+=1),s},t.prototype.choices_count=function(){var t,e,s;if(null!=this.selected_option_count)return this.selected_option_count;for(this.selected_option_count=0,t=0,e=(s=this.form_field.options).length;t0?this.keydown_backstroke():this.pending_backstroke||(this.result_clear_highlight(),this.results_search());break;case 13:t.preventDefault(),this.results_showing&&this.result_select(t);break;case 27:this.results_showing&&this.results_hide();break;case 9:case 16:case 17:case 18:case 38:case 40:case 91:break;default:this.results_search()}},t.prototype.clipboard_event_checker=function(t){if(!this.is_disabled)return setTimeout(function(t){return function(){return t.results_search()}}(this),50)},t.prototype.container_width=function(){return null!=this.options.width?this.options.width:this.form_field.offsetWidth+"px"},t.prototype.include_option_in_results=function(t){return!(this.is_multiple&&!this.display_selected_options&&t.selected)&&(!(!this.display_disabled_options&&t.disabled)&&!t.empty)},t.prototype.search_results_touchstart=function(t){return this.touch_started=!0,this.search_results_mouseover(t)},t.prototype.search_results_touchmove=function(t){return this.touch_started=!1,this.search_results_mouseout(t)},t.prototype.search_results_touchend=function(t){if(this.touch_started)return this.search_results_mouseup(t)},t.prototype.outerHTML=function(t){var e;return t.outerHTML?t.outerHTML:((e=document.createElement("div")).appendChild(t),e.innerHTML)},t.prototype.get_single_html=function(){return'\n '+this.default_text+'\n
      \n
      \n
      \n \n
        \n
        '},t.prototype.get_multi_html=function(){return'
          \n
        • \n \n
        • \n
        \n
        \n
          \n
          '},t.prototype.get_no_results_html=function(t){return'
        • \n '+this.results_none_found+" "+this.escape_html(t)+"\n
        • "},t.browser_is_supported=function(){return"Microsoft Internet Explorer"===window.navigator.appName?document.documentMode>=8:!(/iP(od|hone)/i.test(window.navigator.userAgent)||/IEMobile/i.test(window.navigator.userAgent)||/Windows Phone/i.test(window.navigator.userAgent)||/BlackBerry/i.test(window.navigator.userAgent)||/BB10/i.test(window.navigator.userAgent)||/Android.*Mobile/i.test(window.navigator.userAgent))},t.default_multiple_text="Select Some Options",t.default_single_text="Select an Option",t.default_no_result_text="No results match",t}(),(t=jQuery).fn.extend({chosen:function(i){return e.browser_is_supported()?this.each(function(e){var n,r;r=(n=t(this)).data("chosen"),"destroy"!==i?r instanceof s||n.data("chosen",new s(this,i)):r instanceof s&&r.destroy()}):this}}),s=function(s){function n(){return n.__super__.constructor.apply(this,arguments)}return r(n,e),n.prototype.setup=function(){return this.form_field_jq=t(this.form_field),this.current_selectedIndex=this.form_field.selectedIndex},n.prototype.set_up_html=function(){var e,s;return(e=["chosen-container"]).push("chosen-container-"+(this.is_multiple?"multi":"single")),this.inherit_select_classes&&this.form_field.className&&e.push(this.form_field.className),this.is_rtl&&e.push("chosen-rtl"),s={"class":e.join(" "),title:this.form_field.title},this.form_field.id.length&&(s.id=this.form_field.id.replace(/[^\w]/g,"_")+"_chosen"),this.container=t("
          ",s),this.container.width(this.container_width()),this.is_multiple?this.container.html(this.get_multi_html()):this.container.html(this.get_single_html()),this.form_field_jq.hide().after(this.container),this.dropdown=this.container.find("div.chosen-drop").first(),this.search_field=this.container.find("input").first(),this.search_results=this.container.find("ul.chosen-results").first(),this.search_field_scale(),this.search_no_results=this.container.find("li.no-results").first(),this.is_multiple?(this.search_choices=this.container.find("ul.chosen-choices").first(),this.search_container=this.container.find("li.search-field").first()):(this.search_container=this.container.find("div.chosen-search").first(),this.selected_item=this.container.find(".chosen-single").first()),this.results_build(),this.set_tab_index(),this.set_label_behavior()},n.prototype.on_ready=function(){return this.form_field_jq.trigger("chosen:ready",{chosen:this})},n.prototype.register_observers=function(){return this.container.on("touchstart.chosen",function(t){return function(e){t.container_mousedown(e)}}(this)),this.container.on("touchend.chosen",function(t){return function(e){t.container_mouseup(e)}}(this)),this.container.on("mousedown.chosen",function(t){return function(e){t.container_mousedown(e)}}(this)),this.container.on("mouseup.chosen",function(t){return function(e){t.container_mouseup(e)}}(this)),this.container.on("mouseenter.chosen",function(t){return function(e){t.mouse_enter(e)}}(this)),this.container.on("mouseleave.chosen",function(t){return function(e){t.mouse_leave(e)}}(this)),this.search_results.on("mouseup.chosen",function(t){return function(e){t.search_results_mouseup(e)}}(this)),this.search_results.on("mouseover.chosen",function(t){return function(e){t.search_results_mouseover(e)}}(this)),this.search_results.on("mouseout.chosen",function(t){return function(e){t.search_results_mouseout(e)}}(this)),this.search_results.on("mousewheel.chosen DOMMouseScroll.chosen",function(t){return function(e){t.search_results_mousewheel(e)}}(this)),this.search_results.on("touchstart.chosen",function(t){return function(e){t.search_results_touchstart(e)}}(this)),this.search_results.on("touchmove.chosen",function(t){return function(e){t.search_results_touchmove(e)}}(this)),this.search_results.on("touchend.chosen",function(t){return function(e){t.search_results_touchend(e)}}(this)),this.form_field_jq.on("chosen:updated.chosen",function(t){return function(e){t.results_update_field(e)}}(this)),this.form_field_jq.on("chosen:activate.chosen",function(t){return function(e){t.activate_field(e)}}(this)),this.form_field_jq.on("chosen:open.chosen",function(t){return function(e){t.container_mousedown(e)}}(this)),this.form_field_jq.on("chosen:close.chosen",function(t){return function(e){t.close_field(e)}}(this)),this.search_field.on("blur.chosen",function(t){return function(e){t.input_blur(e)}}(this)),this.search_field.on("keyup.chosen",function(t){return function(e){t.keyup_checker(e)}}(this)),this.search_field.on("keydown.chosen",function(t){return function(e){t.keydown_checker(e)}}(this)),this.search_field.on("focus.chosen",function(t){return function(e){t.input_focus(e)}}(this)),this.search_field.on("cut.chosen",function(t){return function(e){t.clipboard_event_checker(e)}}(this)),this.search_field.on("paste.chosen",function(t){return function(e){t.clipboard_event_checker(e)}}(this)),this.is_multiple?this.search_choices.on("click.chosen",function(t){return function(e){t.choices_click(e)}}(this)):this.container.on("click.chosen",function(t){t.preventDefault()})},n.prototype.destroy=function(){return t(this.container[0].ownerDocument).off("click.chosen",this.click_test_action),this.form_field_label.length>0&&this.form_field_label.off("click.chosen"),this.search_field[0].tabIndex&&(this.form_field_jq[0].tabIndex=this.search_field[0].tabIndex),this.container.remove(),this.form_field_jq.removeData("chosen"),this.form_field_jq.show()},n.prototype.search_field_disabled=function(){return this.is_disabled=this.form_field.disabled||this.form_field_jq.parents("fieldset").is(":disabled"),this.container.toggleClass("chosen-disabled",this.is_disabled),this.search_field[0].disabled=this.is_disabled,this.is_multiple||this.selected_item.off("focus.chosen",this.activate_field),this.is_disabled?this.close_field():this.is_multiple?void 0:this.selected_item.on("focus.chosen",this.activate_field)},n.prototype.container_mousedown=function(e){var s;if(!this.is_disabled)return!e||"mousedown"!==(s=e.type)&&"touchstart"!==s||this.results_showing||e.preventDefault(),null!=e&&t(e.target).hasClass("search-choice-close")?void 0:(this.active_field?this.is_multiple||!e||t(e.target)[0]!==this.selected_item[0]&&!t(e.target).parents("a.chosen-single").length||(e.preventDefault(),this.results_toggle()):(this.is_multiple&&this.search_field.val(""),t(this.container[0].ownerDocument).on("click.chosen",this.click_test_action),this.results_show()),this.activate_field())},n.prototype.container_mouseup=function(t){if("ABBR"===t.target.nodeName&&!this.is_disabled)return this.results_reset(t)},n.prototype.search_results_mousewheel=function(t){var e;if(t.originalEvent&&(e=t.originalEvent.deltaY||-t.originalEvent.wheelDelta||t.originalEvent.detail),null!=e)return t.preventDefault(),"DOMMouseScroll"===t.type&&(e*=40),this.search_results.scrollTop(e+this.search_results.scrollTop())},n.prototype.blur_test=function(t){if(!this.active_field&&this.container.hasClass("chosen-container-active"))return this.close_field()},n.prototype.close_field=function(){return t(this.container[0].ownerDocument).off("click.chosen",this.click_test_action),this.active_field=!1,this.results_hide(),this.container.removeClass("chosen-container-active"),this.clear_backstroke(),this.show_search_field_default(),this.search_field_scale(),this.search_field.blur()},n.prototype.activate_field=function(){if(!this.is_disabled)return this.container.addClass("chosen-container-active"),this.active_field=!0,this.search_field.val(this.search_field.val()),this.search_field.focus()},n.prototype.test_active_click=function(e){var s;return(s=t(e.target).closest(".chosen-container")).length&&this.container[0]===s[0]?this.active_field=!0:this.close_field()},n.prototype.results_build=function(){return this.parsing=!0,this.selected_option_count=null,this.results_data=i.select_to_array(this.form_field),this.is_multiple?this.search_choices.find("li.search-choice").remove():(this.single_set_selected_text(),this.disable_search||this.form_field.options.length<=this.disable_search_threshold?(this.search_field[0].readOnly=!0,this.container.addClass("chosen-container-single-nosearch")):(this.search_field[0].readOnly=!1,this.container.removeClass("chosen-container-single-nosearch"))),this.update_results_content(this.results_option_build({first:!0})),this.search_field_disabled(),this.show_search_field_default(),this.search_field_scale(),this.parsing=!1},n.prototype.result_do_highlight=function(t){var e,s,i,n,r;if(t.length){if(this.result_clear_highlight(),this.result_highlight=t,this.result_highlight.addClass("highlighted"),i=parseInt(this.search_results.css("maxHeight"),10),r=this.search_results.scrollTop(),n=i+r,s=this.result_highlight.position().top+this.search_results.scrollTop(),(e=s+this.result_highlight.outerHeight())>=n)return this.search_results.scrollTop(e-i>0?e-i:0);if(s0)return this.form_field_label.on("click.chosen",this.label_click_handler)},n.prototype.show_search_field_default=function(){return this.is_multiple&&this.choices_count()<1&&!this.active_field?(this.search_field.val(this.default_text),this.search_field.addClass("default")):(this.search_field.val(""),this.search_field.removeClass("default"))},n.prototype.search_results_mouseup=function(e){var s;if((s=t(e.target).hasClass("active-result")?t(e.target):t(e.target).parents(".active-result").first()).length)return this.result_highlight=s,this.result_select(e),this.search_field.focus()},n.prototype.search_results_mouseover=function(e){var s;if(s=t(e.target).hasClass("active-result")?t(e.target):t(e.target).parents(".active-result").first())return this.result_do_highlight(s)},n.prototype.search_results_mouseout=function(e){if(t(e.target).hasClass("active-result")||t(e.target).parents(".active-result").first())return this.result_clear_highlight()},n.prototype.choice_build=function(e){var s,i;return s=t("
        • ",{"class":"search-choice"}).html(""+this.choice_label(e)+""),e.disabled?s.addClass("search-choice-disabled"):((i=t("",{"class":"search-choice-close","data-option-array-index":e.array_index})).on("click.chosen",function(t){return function(e){return t.choice_destroy_link_click(e)}}(this)),s.append(i)),this.search_container.before(s)},n.prototype.choice_destroy_link_click=function(e){if(e.preventDefault(),e.stopPropagation(),!this.is_disabled)return this.choice_destroy(t(e.target))},n.prototype.choice_destroy=function(t){if(this.result_deselect(t[0].getAttribute("data-option-array-index")))return this.active_field?this.search_field.focus():this.show_search_field_default(),this.is_multiple&&this.choices_count()>0&&this.get_search_field_value().length<1&&this.results_hide(),t.parents("li").first().remove(),this.search_field_scale()},n.prototype.results_reset=function(){if(this.reset_single_select_options(),this.form_field.options[0].selected=!0,this.single_set_selected_text(),this.show_search_field_default(),this.results_reset_cleanup(),this.trigger_form_field_change(),this.active_field)return this.results_hide()},n.prototype.results_reset_cleanup=function(){return this.current_selectedIndex=this.form_field.selectedIndex,this.selected_item.find("abbr").remove()},n.prototype.result_select=function(t){var e,s;if(this.result_highlight)return e=this.result_highlight,this.result_clear_highlight(),this.is_multiple&&this.max_selected_options<=this.choices_count()?(this.form_field_jq.trigger("chosen:maxselected",{chosen:this}),!1):(this.is_multiple?e.removeClass("active-result"):this.reset_single_select_options(),e.addClass("result-selected"),s=this.results_data[e[0].getAttribute("data-option-array-index")],s.selected=!0,this.form_field.options[s.options_index].selected=!0,this.selected_option_count=null,this.is_multiple?this.choice_build(s):this.single_set_selected_text(this.choice_label(s)),this.is_multiple&&(!this.hide_results_on_select||t.metaKey||t.ctrlKey)?t.metaKey||t.ctrlKey?this.winnow_results({skip_highlight:!0}):(this.search_field.val(""),this.winnow_results()):(this.results_hide(),this.show_search_field_default()),(this.is_multiple||this.form_field.selectedIndex!==this.current_selectedIndex)&&this.trigger_form_field_change({selected:this.form_field.options[s.options_index].value}),this.current_selectedIndex=this.form_field.selectedIndex,t.preventDefault(),this.search_field_scale())},n.prototype.single_set_selected_text=function(t){return null==t&&(t=this.default_text),t===this.default_text?this.selected_item.addClass("chosen-default"):(this.single_deselect_control_build(),this.selected_item.removeClass("chosen-default")),this.selected_item.find("span").html(t)},n.prototype.result_deselect=function(t){var e;return e=this.results_data[t],!this.form_field.options[e.options_index].disabled&&(e.selected=!1,this.form_field.options[e.options_index].selected=!1,this.selected_option_count=null,this.result_clear_highlight(),this.results_showing&&this.winnow_results(),this.trigger_form_field_change({deselected:this.form_field.options[e.options_index].value}),this.search_field_scale(),!0)},n.prototype.single_deselect_control_build=function(){if(this.allow_single_deselect)return this.selected_item.find("abbr").length||this.selected_item.find("span").first().after(''),this.selected_item.addClass("chosen-single-with-deselect")},n.prototype.get_search_field_value=function(){return this.search_field.val()},n.prototype.get_search_text=function(){return t.trim(this.get_search_field_value())},n.prototype.escape_html=function(e){return t("
          ").text(e).html()},n.prototype.winnow_results_set_highlight=function(){var t,e;if(e=this.is_multiple?[]:this.search_results.find(".result-selected.active-result"),null!=(t=e.length?e.first():this.search_results.find(".active-result").first()))return this.result_do_highlight(t)},n.prototype.no_results=function(t){var e;return e=this.get_no_results_html(t),this.search_results.append(e),this.form_field_jq.trigger("chosen:no_results",{chosen:this})},n.prototype.no_results_clear=function(){return this.search_results.find(".no-results").remove()},n.prototype.keydown_arrow=function(){var t;return this.results_showing&&this.result_highlight?(t=this.result_highlight.nextAll("li.active-result").first())?this.result_do_highlight(t):void 0:this.results_show()},n.prototype.keyup_arrow=function(){var t;return this.results_showing||this.is_multiple?this.result_highlight?(t=this.result_highlight.prevAll("li.active-result")).length?this.result_do_highlight(t.first()):(this.choices_count()>0&&this.results_hide(),this.result_clear_highlight()):void 0:this.results_show()},n.prototype.keydown_backstroke=function(){var t;return this.pending_backstroke?(this.choice_destroy(this.pending_backstroke.find("a").first()),this.clear_backstroke()):(t=this.search_container.siblings("li.search-choice").last()).length&&!t.hasClass("search-choice-disabled")?(this.pending_backstroke=t,this.single_backstroke_delete?this.keydown_backstroke():this.pending_backstroke.addClass("search-choice-focus")):void 0},n.prototype.clear_backstroke=function(){return this.pending_backstroke&&this.pending_backstroke.removeClass("search-choice-focus"),this.pending_backstroke=null},n.prototype.search_field_scale=function(){var e,s,i,n,r,o,h;if(this.is_multiple){for(r={position:"absolute",left:"-1000px",top:"-1000px",display:"none",whiteSpace:"pre"},s=0,i=(o=["fontSize","fontStyle","fontWeight","fontFamily","lineHeight","textTransform","letterSpacing"]).length;s").css(r)).text(this.get_search_field_value()),t("body").append(e),h=e.width()+25,e.remove(),this.container.is(":visible")&&(h=Math.min(this.container.outerWidth()-10,h)),this.search_field.width(h)}},n.prototype.trigger_form_field_change=function(t){return this.form_field_jq.trigger("input",t),this.form_field_jq.trigger("change",t)},n}()}).call(this); \ No newline at end of file diff --git a/web/skins/classic/js/chosen/chosen.min.css b/web/skins/classic/js/chosen/chosen.min.css index 779d83d91..1c68ebb1c 100644 --- a/web/skins/classic/js/chosen/chosen.min.css +++ b/web/skins/classic/js/chosen/chosen.min.css @@ -2,10 +2,10 @@ Chosen, a Select Box Enhancer for jQuery and Prototype by Patrick Filler for Harvest, http://getharvest.com -Version 1.8.2 +Version 1.8.7 Full source at https://github.com/harvesthq/chosen -Copyright (c) 2011-2017 Harvest http://getharvest.com +Copyright (c) 2011-2018 Harvest http://getharvest.com MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md This file is generated by `grunt build`, do not edit it by hand. -*/.chosen-container{position:relative;display:inline-block;vertical-align:middle;font-size:13px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.chosen-container *{-webkit-box-sizing:border-box;box-sizing:border-box}.chosen-container .chosen-drop{position:absolute;top:100%;z-index:1010;width:100%;border:1px solid #aaa;border-top:0;background:#fff;-webkit-box-shadow:0 4px 5px rgba(0,0,0,.15);box-shadow:0 4px 5px rgba(0,0,0,.15);clip:rect(0,0,0,0)}.chosen-container.chosen-with-drop .chosen-drop{clip:auto}.chosen-container a{cursor:pointer}.chosen-container .chosen-single .group-name,.chosen-container .search-choice .group-name{margin-right:4px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-weight:400;color:#999}.chosen-container .chosen-single .group-name:after,.chosen-container .search-choice .group-name:after{content:":";padding-left:2px;vertical-align:top}.chosen-container-single .chosen-single{position:relative;display:block;overflow:hidden;padding:0 0 0 8px;height:25px;border:1px solid #aaa;border-radius:5px;background-color:#fff;background:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#fff),color-stop(50%,#f6f6f6),color-stop(52%,#eee),to(#f4f4f4));background:linear-gradient(#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background-clip:padding-box;-webkit-box-shadow:0 0 3px #fff inset,0 1px 1px rgba(0,0,0,.1);box-shadow:0 0 3px #fff inset,0 1px 1px rgba(0,0,0,.1);color:#444;text-decoration:none;white-space:nowrap;line-height:24px}.chosen-container-single .chosen-default{color:#999}.chosen-container-single .chosen-single span{display:block;overflow:hidden;margin-right:26px;text-overflow:ellipsis;white-space:nowrap}.chosen-container-single .chosen-single-with-deselect span{margin-right:38px}.chosen-container-single .chosen-single abbr{position:absolute;top:6px;right:26px;display:block;width:12px;height:12px;background:url(chosen-sprite.png) -42px 1px no-repeat;font-size:1px}.chosen-container-single .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single.chosen-disabled .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single .chosen-single div{position:absolute;top:0;right:0;display:block;width:18px;height:100%}.chosen-container-single .chosen-single div b{display:block;width:100%;height:100%;background:url(chosen-sprite.png) no-repeat 0 2px}.chosen-container-single .chosen-search{position:relative;z-index:1010;margin:0;padding:3px 4px;white-space:nowrap}.chosen-container-single .chosen-search input[type=text]{margin:1px 0;padding:4px 20px 4px 5px;width:100%;height:auto;outline:0;border:1px solid #aaa;background:url(chosen-sprite.png) no-repeat 100% -20px;font-size:1em;font-family:sans-serif;line-height:normal;border-radius:0}.chosen-container-single .chosen-drop{margin-top:-1px;border-radius:0 0 4px 4px;background-clip:padding-box}.chosen-container-single.chosen-container-single-nosearch .chosen-search{position:absolute;clip:rect(0,0,0,0)}.chosen-container .chosen-results{color:#444;position:relative;overflow-x:hidden;overflow-y:auto;margin:0 4px 4px 0;padding:0 0 0 4px;max-height:240px;-webkit-overflow-scrolling:touch}.chosen-container .chosen-results li{display:none;margin:0;padding:5px 6px;list-style:none;line-height:15px;word-wrap:break-word;-webkit-touch-callout:none}.chosen-container .chosen-results li.active-result{display:list-item;cursor:pointer}.chosen-container .chosen-results li.disabled-result{display:list-item;color:#ccc;cursor:default}.chosen-container .chosen-results li.highlighted{background-color:#3875d7;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#3875d7),color-stop(90%,#2a62bc));background-image:linear-gradient(#3875d7 20%,#2a62bc 90%);color:#fff}.chosen-container .chosen-results li.no-results{color:#777;display:list-item;background:#f4f4f4}.chosen-container .chosen-results li.group-result{display:list-item;font-weight:700;cursor:default}.chosen-container .chosen-results li.group-option{padding-left:15px}.chosen-container .chosen-results li em{font-style:normal;text-decoration:underline}.chosen-container-multi .chosen-choices{position:relative;overflow:hidden;margin:0;padding:0 5px;width:100%;height:auto;border:1px solid #aaa;background-color:#fff;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(1%,#eee),color-stop(15%,#fff));background-image:linear-gradient(#eee 1%,#fff 15%);cursor:text}.chosen-container-multi .chosen-choices li{float:left;list-style:none}.chosen-container-multi .chosen-choices li.search-field{margin:0;padding:0;white-space:nowrap}.chosen-container-multi .chosen-choices li.search-field input[type=text]{margin:1px 0;padding:0;height:25px;outline:0;border:0!important;background:0 0!important;-webkit-box-shadow:none;box-shadow:none;color:#999;font-size:100%;font-family:sans-serif;line-height:normal;border-radius:0;width:25px}.chosen-container-multi .chosen-choices li.search-choice{position:relative;margin:3px 5px 3px 0;padding:3px 20px 3px 5px;border:1px solid #aaa;max-width:100%;border-radius:3px;background-color:#eee;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#f4f4f4),color-stop(50%,#f0f0f0),color-stop(52%,#e8e8e8),to(#eee));background-image:linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-size:100% 19px;background-repeat:repeat-x;background-clip:padding-box;-webkit-box-shadow:0 0 2px #fff inset,0 1px 0 rgba(0,0,0,.05);box-shadow:0 0 2px #fff inset,0 1px 0 rgba(0,0,0,.05);color:#333;line-height:13px;cursor:default}.chosen-container-multi .chosen-choices li.search-choice span{word-wrap:break-word}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close{position:absolute;top:4px;right:3px;display:block;width:12px;height:12px;background:url(chosen-sprite.png) -42px 1px no-repeat;font-size:1px}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close:hover{background-position:-42px -10px}.chosen-container-multi .chosen-choices li.search-choice-disabled{padding-right:5px;border:1px solid #ccc;background-color:#e4e4e4;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#f4f4f4),color-stop(50%,#f0f0f0),color-stop(52%,#e8e8e8),to(#eee));background-image:linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);color:#666}.chosen-container-multi .chosen-choices li.search-choice-focus{background:#d4d4d4}.chosen-container-multi .chosen-choices li.search-choice-focus .search-choice-close{background-position:-42px -10px}.chosen-container-multi .chosen-results{margin:0;padding:0}.chosen-container-multi .chosen-drop .result-selected{display:list-item;color:#ccc;cursor:default}.chosen-container-active .chosen-single{border:1px solid #5897fb;-webkit-box-shadow:0 0 5px rgba(0,0,0,.3);box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active.chosen-with-drop .chosen-single{border:1px solid #aaa;border-bottom-right-radius:0;border-bottom-left-radius:0;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#eee),color-stop(80%,#fff));background-image:linear-gradient(#eee 20%,#fff 80%);-webkit-box-shadow:0 1px 0 #fff inset;box-shadow:0 1px 0 #fff inset}.chosen-container-active.chosen-with-drop .chosen-single div{border-left:none;background:0 0}.chosen-container-active.chosen-with-drop .chosen-single div b{background-position:-18px 2px}.chosen-container-active .chosen-choices{border:1px solid #5897fb;-webkit-box-shadow:0 0 5px rgba(0,0,0,.3);box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active .chosen-choices li.search-field input[type=text]{color:#222!important}.chosen-disabled{opacity:.5!important;cursor:default}.chosen-disabled .chosen-single{cursor:default}.chosen-disabled .chosen-choices .search-choice .search-choice-close{cursor:default}.chosen-rtl{text-align:right}.chosen-rtl .chosen-single{overflow:visible;padding:0 8px 0 0}.chosen-rtl .chosen-single span{margin-right:0;margin-left:26px;direction:rtl}.chosen-rtl .chosen-single-with-deselect span{margin-left:38px}.chosen-rtl .chosen-single div{right:auto;left:3px}.chosen-rtl .chosen-single abbr{right:auto;left:26px}.chosen-rtl .chosen-choices li{float:right}.chosen-rtl .chosen-choices li.search-field input[type=text]{direction:rtl}.chosen-rtl .chosen-choices li.search-choice{margin:3px 5px 3px 0;padding:3px 5px 3px 19px}.chosen-rtl .chosen-choices li.search-choice .search-choice-close{right:auto;left:4px}.chosen-rtl.chosen-container-single .chosen-results{margin:0 0 4px 4px;padding:0 4px 0 0}.chosen-rtl .chosen-results li.group-option{padding-right:15px;padding-left:0}.chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div{border-right:none}.chosen-rtl .chosen-search input[type=text]{padding:4px 5px 4px 20px;background:url(chosen-sprite.png) no-repeat -30px -20px;direction:rtl}.chosen-rtl.chosen-container-single .chosen-single div b{background-position:6px 2px}.chosen-rtl.chosen-container-single.chosen-with-drop .chosen-single div b{background-position:-12px 2px}@media only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min-resolution:144dpi),only screen and (min-resolution:1.5dppx){.chosen-container .chosen-results-scroll-down span,.chosen-container .chosen-results-scroll-up span,.chosen-container-multi .chosen-choices .search-choice .search-choice-close,.chosen-container-single .chosen-search input[type=text],.chosen-container-single .chosen-single abbr,.chosen-container-single .chosen-single div b,.chosen-rtl .chosen-search input[type=text]{background-image:url(chosen-sprite@2x.png)!important;background-size:52px 37px!important;background-repeat:no-repeat!important}} \ No newline at end of file +*/.chosen-container{position:relative;display:inline-block;vertical-align:middle;font-size:13px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.chosen-container *{-webkit-box-sizing:border-box;box-sizing:border-box}.chosen-container .chosen-drop{position:absolute;top:100%;z-index:1010;width:100%;border:1px solid #aaa;border-top:0;background:#fff;-webkit-box-shadow:0 4px 5px rgba(0,0,0,.15);box-shadow:0 4px 5px rgba(0,0,0,.15);clip:rect(0,0,0,0);-webkit-clip-path:inset(100% 100%);clip-path:inset(100% 100%)}.chosen-container.chosen-with-drop .chosen-drop{clip:auto;-webkit-clip-path:none;clip-path:none}.chosen-container a{cursor:pointer}.chosen-container .chosen-single .group-name,.chosen-container .search-choice .group-name{margin-right:4px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-weight:400;color:#999}.chosen-container .chosen-single .group-name:after,.chosen-container .search-choice .group-name:after{content:":";padding-left:2px;vertical-align:top}.chosen-container-single .chosen-single{position:relative;display:block;overflow:hidden;padding:0 0 0 8px;height:25px;border:1px solid #aaa;border-radius:5px;background-color:#fff;background:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#fff),color-stop(50%,#f6f6f6),color-stop(52%,#eee),to(#f4f4f4));background:linear-gradient(#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background-clip:padding-box;-webkit-box-shadow:0 0 3px #fff inset,0 1px 1px rgba(0,0,0,.1);box-shadow:0 0 3px #fff inset,0 1px 1px rgba(0,0,0,.1);color:#444;text-decoration:none;white-space:nowrap;line-height:24px}.chosen-container-single .chosen-default{color:#999}.chosen-container-single .chosen-single span{display:block;overflow:hidden;margin-right:26px;text-overflow:ellipsis;white-space:nowrap}.chosen-container-single .chosen-single-with-deselect span{margin-right:38px}.chosen-container-single .chosen-single abbr{position:absolute;top:6px;right:26px;display:block;width:12px;height:12px;background:url(chosen-sprite.png) -42px 1px no-repeat;font-size:1px}.chosen-container-single .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single.chosen-disabled .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single .chosen-single div{position:absolute;top:0;right:0;display:block;width:18px;height:100%}.chosen-container-single .chosen-single div b{display:block;width:100%;height:100%;background:url(chosen-sprite.png) no-repeat 0 2px}.chosen-container-single .chosen-search{position:relative;z-index:1010;margin:0;padding:3px 4px;white-space:nowrap}.chosen-container-single .chosen-search input[type=text]{margin:1px 0;padding:4px 20px 4px 5px;width:100%;height:auto;outline:0;border:1px solid #aaa;background:url(chosen-sprite.png) no-repeat 100% -20px;font-size:1em;font-family:sans-serif;line-height:normal;border-radius:0}.chosen-container-single .chosen-drop{margin-top:-1px;border-radius:0 0 4px 4px;background-clip:padding-box}.chosen-container-single.chosen-container-single-nosearch .chosen-search{position:absolute;clip:rect(0,0,0,0);-webkit-clip-path:inset(100% 100%);clip-path:inset(100% 100%)}.chosen-container .chosen-results{color:#444;position:relative;overflow-x:hidden;overflow-y:auto;margin:0 4px 4px 0;padding:0 0 0 4px;max-height:240px;-webkit-overflow-scrolling:touch}.chosen-container .chosen-results li{display:none;margin:0;padding:5px 6px;list-style:none;line-height:15px;word-wrap:break-word;-webkit-touch-callout:none}.chosen-container .chosen-results li.active-result{display:list-item;cursor:pointer}.chosen-container .chosen-results li.disabled-result{display:list-item;color:#ccc;cursor:default}.chosen-container .chosen-results li.highlighted{background-color:#3875d7;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#3875d7),color-stop(90%,#2a62bc));background-image:linear-gradient(#3875d7 20%,#2a62bc 90%);color:#fff}.chosen-container .chosen-results li.no-results{color:#777;display:list-item;background:#f4f4f4}.chosen-container .chosen-results li.group-result{display:list-item;font-weight:700;cursor:default}.chosen-container .chosen-results li.group-option{padding-left:15px}.chosen-container .chosen-results li em{font-style:normal;text-decoration:underline}.chosen-container-multi .chosen-choices{position:relative;overflow:hidden;margin:0;padding:0 5px;width:100%;height:auto;border:1px solid #aaa;background-color:#fff;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(1%,#eee),color-stop(15%,#fff));background-image:linear-gradient(#eee 1%,#fff 15%);cursor:text}.chosen-container-multi .chosen-choices li{float:left;list-style:none}.chosen-container-multi .chosen-choices li.search-field{margin:0;padding:0;white-space:nowrap}.chosen-container-multi .chosen-choices li.search-field input[type=text]{margin:1px 0;padding:0;height:25px;outline:0;border:0!important;background:0 0!important;-webkit-box-shadow:none;box-shadow:none;color:#999;font-size:100%;font-family:sans-serif;line-height:normal;border-radius:0;width:25px}.chosen-container-multi .chosen-choices li.search-choice{position:relative;margin:3px 5px 3px 0;padding:3px 20px 3px 5px;border:1px solid #aaa;max-width:100%;border-radius:3px;background-color:#eee;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#f4f4f4),color-stop(50%,#f0f0f0),color-stop(52%,#e8e8e8),to(#eee));background-image:linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-size:100% 19px;background-repeat:repeat-x;background-clip:padding-box;-webkit-box-shadow:0 0 2px #fff inset,0 1px 0 rgba(0,0,0,.05);box-shadow:0 0 2px #fff inset,0 1px 0 rgba(0,0,0,.05);color:#333;line-height:13px;cursor:default}.chosen-container-multi .chosen-choices li.search-choice span{word-wrap:break-word}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close{position:absolute;top:4px;right:3px;display:block;width:12px;height:12px;background:url(chosen-sprite.png) -42px 1px no-repeat;font-size:1px}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close:hover{background-position:-42px -10px}.chosen-container-multi .chosen-choices li.search-choice-disabled{padding-right:5px;border:1px solid #ccc;background-color:#e4e4e4;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#f4f4f4),color-stop(50%,#f0f0f0),color-stop(52%,#e8e8e8),to(#eee));background-image:linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);color:#666}.chosen-container-multi .chosen-choices li.search-choice-focus{background:#d4d4d4}.chosen-container-multi .chosen-choices li.search-choice-focus .search-choice-close{background-position:-42px -10px}.chosen-container-multi .chosen-results{margin:0;padding:0}.chosen-container-multi .chosen-drop .result-selected{display:list-item;color:#ccc;cursor:default}.chosen-container-active .chosen-single{border:1px solid #5897fb;-webkit-box-shadow:0 0 5px rgba(0,0,0,.3);box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active.chosen-with-drop .chosen-single{border:1px solid #aaa;border-bottom-right-radius:0;border-bottom-left-radius:0;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(20%,#eee),color-stop(80%,#fff));background-image:linear-gradient(#eee 20%,#fff 80%);-webkit-box-shadow:0 1px 0 #fff inset;box-shadow:0 1px 0 #fff inset}.chosen-container-active.chosen-with-drop .chosen-single div{border-left:none;background:0 0}.chosen-container-active.chosen-with-drop .chosen-single div b{background-position:-18px 2px}.chosen-container-active .chosen-choices{border:1px solid #5897fb;-webkit-box-shadow:0 0 5px rgba(0,0,0,.3);box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active .chosen-choices li.search-field input[type=text]{color:#222!important}.chosen-disabled{opacity:.5!important;cursor:default}.chosen-disabled .chosen-single{cursor:default}.chosen-disabled .chosen-choices .search-choice .search-choice-close{cursor:default}.chosen-rtl{text-align:right}.chosen-rtl .chosen-single{overflow:visible;padding:0 8px 0 0}.chosen-rtl .chosen-single span{margin-right:0;margin-left:26px;direction:rtl}.chosen-rtl .chosen-single-with-deselect span{margin-left:38px}.chosen-rtl .chosen-single div{right:auto;left:3px}.chosen-rtl .chosen-single abbr{right:auto;left:26px}.chosen-rtl .chosen-choices li{float:right}.chosen-rtl .chosen-choices li.search-field input[type=text]{direction:rtl}.chosen-rtl .chosen-choices li.search-choice{margin:3px 5px 3px 0;padding:3px 5px 3px 19px}.chosen-rtl .chosen-choices li.search-choice .search-choice-close{right:auto;left:4px}.chosen-rtl.chosen-container-single .chosen-results{margin:0 0 4px 4px;padding:0 4px 0 0}.chosen-rtl .chosen-results li.group-option{padding-right:15px;padding-left:0}.chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div{border-right:none}.chosen-rtl .chosen-search input[type=text]{padding:4px 5px 4px 20px;background:url(chosen-sprite.png) no-repeat -30px -20px;direction:rtl}.chosen-rtl.chosen-container-single .chosen-single div b{background-position:6px 2px}.chosen-rtl.chosen-container-single.chosen-with-drop .chosen-single div b{background-position:-12px 2px}@media only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min-resolution:144dpi),only screen and (min-resolution:1.5dppx){.chosen-container .chosen-results-scroll-down span,.chosen-container .chosen-results-scroll-up span,.chosen-container-multi .chosen-choices .search-choice .search-choice-close,.chosen-container-single .chosen-search input[type=text],.chosen-container-single .chosen-single abbr,.chosen-container-single .chosen-single div b,.chosen-rtl .chosen-search input[type=text]{background-image:url(chosen-sprite@2x.png)!important;background-size:52px 37px!important;background-repeat:no-repeat!important}} \ No newline at end of file diff --git a/web/skins/classic/js/chosen/chosen.proto.js b/web/skins/classic/js/chosen/chosen.proto.js deleted file mode 100644 index ea517ad4f..000000000 --- a/web/skins/classic/js/chosen/chosen.proto.js +++ /dev/null @@ -1,1389 +0,0 @@ -/*! -Chosen, a Select Box Enhancer for jQuery and Prototype -by Patrick Filler for Harvest, http://getharvest.com - -Version 1.8.2 -Full source at https://github.com/harvesthq/chosen -Copyright (c) 2011-2017 Harvest http://getharvest.com - -MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md -This file is generated by `grunt build`, do not edit it by hand. -*/ - -(function() { - var AbstractChosen, SelectParser, - bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, - extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, - hasProp = {}.hasOwnProperty; - - SelectParser = (function() { - function SelectParser() { - this.options_index = 0; - this.parsed = []; - } - - SelectParser.prototype.add_node = function(child) { - if (child.nodeName.toUpperCase() === "OPTGROUP") { - return this.add_group(child); - } else { - return this.add_option(child); - } - }; - - SelectParser.prototype.add_group = function(group) { - var group_position, i, len, option, ref, results1; - group_position = this.parsed.length; - this.parsed.push({ - array_index: group_position, - group: true, - label: group.label, - title: group.title ? group.title : void 0, - children: 0, - disabled: group.disabled, - classes: group.className - }); - ref = group.childNodes; - results1 = []; - for (i = 0, len = ref.length; i < len; i++) { - option = ref[i]; - results1.push(this.add_option(option, group_position, group.disabled)); - } - return results1; - }; - - SelectParser.prototype.add_option = function(option, group_position, group_disabled) { - if (option.nodeName.toUpperCase() === "OPTION") { - if (option.text !== "") { - if (group_position != null) { - this.parsed[group_position].children += 1; - } - this.parsed.push({ - array_index: this.parsed.length, - options_index: this.options_index, - value: option.value, - text: option.text, - html: option.innerHTML, - title: option.title ? option.title : void 0, - selected: option.selected, - disabled: group_disabled === true ? group_disabled : option.disabled, - group_array_index: group_position, - group_label: group_position != null ? this.parsed[group_position].label : null, - classes: option.className, - style: option.style.cssText - }); - } else { - this.parsed.push({ - array_index: this.parsed.length, - options_index: this.options_index, - empty: true - }); - } - return this.options_index += 1; - } - }; - - return SelectParser; - - })(); - - SelectParser.select_to_array = function(select) { - var child, i, len, parser, ref; - parser = new SelectParser(); - ref = select.childNodes; - for (i = 0, len = ref.length; i < len; i++) { - child = ref[i]; - parser.add_node(child); - } - return parser.parsed; - }; - - AbstractChosen = (function() { - function AbstractChosen(form_field, options1) { - this.form_field = form_field; - this.options = options1 != null ? options1 : {}; - this.label_click_handler = bind(this.label_click_handler, this); - if (!AbstractChosen.browser_is_supported()) { - return; - } - this.is_multiple = this.form_field.multiple; - this.set_default_text(); - this.set_default_values(); - this.setup(); - this.set_up_html(); - this.register_observers(); - this.on_ready(); - } - - AbstractChosen.prototype.set_default_values = function() { - this.click_test_action = (function(_this) { - return function(evt) { - return _this.test_active_click(evt); - }; - })(this); - this.activate_action = (function(_this) { - return function(evt) { - return _this.activate_field(evt); - }; - })(this); - this.active_field = false; - this.mouse_on_container = false; - this.results_showing = false; - this.result_highlighted = null; - this.is_rtl = this.options.rtl || /\bchosen-rtl\b/.test(this.form_field.className); - this.allow_single_deselect = (this.options.allow_single_deselect != null) && (this.form_field.options[0] != null) && this.form_field.options[0].text === "" ? this.options.allow_single_deselect : false; - this.disable_search_threshold = this.options.disable_search_threshold || 0; - this.disable_search = this.options.disable_search || false; - this.enable_split_word_search = this.options.enable_split_word_search != null ? this.options.enable_split_word_search : true; - this.group_search = this.options.group_search != null ? this.options.group_search : true; - this.search_contains = this.options.search_contains || false; - this.single_backstroke_delete = this.options.single_backstroke_delete != null ? this.options.single_backstroke_delete : true; - this.max_selected_options = this.options.max_selected_options || Infinity; - this.inherit_select_classes = this.options.inherit_select_classes || false; - this.display_selected_options = this.options.display_selected_options != null ? this.options.display_selected_options : true; - this.display_disabled_options = this.options.display_disabled_options != null ? this.options.display_disabled_options : true; - this.include_group_label_in_selected = this.options.include_group_label_in_selected || false; - this.max_shown_results = this.options.max_shown_results || Number.POSITIVE_INFINITY; - this.case_sensitive_search = this.options.case_sensitive_search || false; - return this.hide_results_on_select = this.options.hide_results_on_select != null ? this.options.hide_results_on_select : true; - }; - - AbstractChosen.prototype.set_default_text = function() { - if (this.form_field.getAttribute("data-placeholder")) { - this.default_text = this.form_field.getAttribute("data-placeholder"); - } else if (this.is_multiple) { - this.default_text = this.options.placeholder_text_multiple || this.options.placeholder_text || AbstractChosen.default_multiple_text; - } else { - this.default_text = this.options.placeholder_text_single || this.options.placeholder_text || AbstractChosen.default_single_text; - } - this.default_text = this.escape_html(this.default_text); - return this.results_none_found = this.form_field.getAttribute("data-no_results_text") || this.options.no_results_text || AbstractChosen.default_no_result_text; - }; - - AbstractChosen.prototype.choice_label = function(item) { - if (this.include_group_label_in_selected && (item.group_label != null)) { - return "" + item.group_label + "" + item.html; - } else { - return item.html; - } - }; - - AbstractChosen.prototype.mouse_enter = function() { - return this.mouse_on_container = true; - }; - - AbstractChosen.prototype.mouse_leave = function() { - return this.mouse_on_container = false; - }; - - AbstractChosen.prototype.input_focus = function(evt) { - if (this.is_multiple) { - if (!this.active_field) { - return setTimeout(((function(_this) { - return function() { - return _this.container_mousedown(); - }; - })(this)), 50); - } - } else { - if (!this.active_field) { - return this.activate_field(); - } - } - }; - - AbstractChosen.prototype.input_blur = function(evt) { - if (!this.mouse_on_container) { - this.active_field = false; - return setTimeout(((function(_this) { - return function() { - return _this.blur_test(); - }; - })(this)), 100); - } - }; - - AbstractChosen.prototype.label_click_handler = function(evt) { - if (this.is_multiple) { - return this.container_mousedown(evt); - } else { - return this.activate_field(); - } - }; - - AbstractChosen.prototype.results_option_build = function(options) { - var content, data, data_content, i, len, ref, shown_results; - content = ''; - shown_results = 0; - ref = this.results_data; - for (i = 0, len = ref.length; i < len; i++) { - data = ref[i]; - data_content = ''; - if (data.group) { - data_content = this.result_add_group(data); - } else { - data_content = this.result_add_option(data); - } - if (data_content !== '') { - shown_results++; - content += data_content; - } - if (options != null ? options.first : void 0) { - if (data.selected && this.is_multiple) { - this.choice_build(data); - } else if (data.selected && !this.is_multiple) { - this.single_set_selected_text(this.choice_label(data)); - } - } - if (shown_results >= this.max_shown_results) { - break; - } - } - return content; - }; - - AbstractChosen.prototype.result_add_option = function(option) { - var classes, option_el; - if (!option.search_match) { - return ''; - } - if (!this.include_option_in_results(option)) { - return ''; - } - classes = []; - if (!option.disabled && !(option.selected && this.is_multiple)) { - classes.push("active-result"); - } - if (option.disabled && !(option.selected && this.is_multiple)) { - classes.push("disabled-result"); - } - if (option.selected) { - classes.push("result-selected"); - } - if (option.group_array_index != null) { - classes.push("group-option"); - } - if (option.classes !== "") { - classes.push(option.classes); - } - option_el = document.createElement("li"); - option_el.className = classes.join(" "); - option_el.style.cssText = option.style; - option_el.setAttribute("data-option-array-index", option.array_index); - option_el.innerHTML = option.highlighted_html || option.html; - if (option.title) { - option_el.title = option.title; - } - return this.outerHTML(option_el); - }; - - AbstractChosen.prototype.result_add_group = function(group) { - var classes, group_el; - if (!(group.search_match || group.group_match)) { - return ''; - } - if (!(group.active_options > 0)) { - return ''; - } - classes = []; - classes.push("group-result"); - if (group.classes) { - classes.push(group.classes); - } - group_el = document.createElement("li"); - group_el.className = classes.join(" "); - group_el.innerHTML = group.highlighted_html || this.escape_html(group.label); - if (group.title) { - group_el.title = group.title; - } - return this.outerHTML(group_el); - }; - - AbstractChosen.prototype.results_update_field = function() { - this.set_default_text(); - if (!this.is_multiple) { - this.results_reset_cleanup(); - } - this.result_clear_highlight(); - this.results_build(); - if (this.results_showing) { - return this.winnow_results(); - } - }; - - AbstractChosen.prototype.reset_single_select_options = function() { - var i, len, ref, result, results1; - ref = this.results_data; - results1 = []; - for (i = 0, len = ref.length; i < len; i++) { - result = ref[i]; - if (result.selected) { - results1.push(result.selected = false); - } else { - results1.push(void 0); - } - } - return results1; - }; - - AbstractChosen.prototype.results_toggle = function() { - if (this.results_showing) { - return this.results_hide(); - } else { - return this.results_show(); - } - }; - - AbstractChosen.prototype.results_search = function(evt) { - if (this.results_showing) { - return this.winnow_results(); - } else { - return this.results_show(); - } - }; - - AbstractChosen.prototype.winnow_results = function() { - var escapedQuery, fix, i, len, option, prefix, query, ref, regex, results, results_group, search_match, startpos, suffix, text; - this.no_results_clear(); - results = 0; - query = this.get_search_text(); - escapedQuery = query.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); - regex = this.get_search_regex(escapedQuery); - ref = this.results_data; - for (i = 0, len = ref.length; i < len; i++) { - option = ref[i]; - option.search_match = false; - results_group = null; - search_match = null; - option.highlighted_html = ''; - if (this.include_option_in_results(option)) { - if (option.group) { - option.group_match = false; - option.active_options = 0; - } - if ((option.group_array_index != null) && this.results_data[option.group_array_index]) { - results_group = this.results_data[option.group_array_index]; - if (results_group.active_options === 0 && results_group.search_match) { - results += 1; - } - results_group.active_options += 1; - } - text = option.group ? option.label : option.text; - if (!(option.group && !this.group_search)) { - search_match = this.search_string_match(text, regex); - option.search_match = search_match != null; - if (option.search_match && !option.group) { - results += 1; - } - if (option.search_match) { - if (query.length) { - startpos = search_match.index; - prefix = text.slice(0, startpos); - fix = text.slice(startpos, startpos + query.length); - suffix = text.slice(startpos + query.length); - option.highlighted_html = (this.escape_html(prefix)) + "" + (this.escape_html(fix)) + "" + (this.escape_html(suffix)); - } - if (results_group != null) { - results_group.group_match = true; - } - } else if ((option.group_array_index != null) && this.results_data[option.group_array_index].search_match) { - option.search_match = true; - } - } - } - } - this.result_clear_highlight(); - if (results < 1 && query.length) { - this.update_results_content(""); - return this.no_results(query); - } else { - this.update_results_content(this.results_option_build()); - return this.winnow_results_set_highlight(); - } - }; - - AbstractChosen.prototype.get_search_regex = function(escaped_search_string) { - var regex_flag, regex_string; - regex_string = this.search_contains ? escaped_search_string : "(^|\\s|\\b)" + escaped_search_string + "[^\\s]*"; - if (!(this.enable_split_word_search || this.search_contains)) { - regex_string = "^" + regex_string; - } - regex_flag = this.case_sensitive_search ? "" : "i"; - return new RegExp(regex_string, regex_flag); - }; - - AbstractChosen.prototype.search_string_match = function(search_string, regex) { - var match; - match = regex.exec(search_string); - if (!this.search_contains && (match != null ? match[1] : void 0)) { - match.index += 1; - } - return match; - }; - - AbstractChosen.prototype.choices_count = function() { - var i, len, option, ref; - if (this.selected_option_count != null) { - return this.selected_option_count; - } - this.selected_option_count = 0; - ref = this.form_field.options; - for (i = 0, len = ref.length; i < len; i++) { - option = ref[i]; - if (option.selected) { - this.selected_option_count += 1; - } - } - return this.selected_option_count; - }; - - AbstractChosen.prototype.choices_click = function(evt) { - evt.preventDefault(); - this.activate_field(); - if (!(this.results_showing || this.is_disabled)) { - return this.results_show(); - } - }; - - AbstractChosen.prototype.keydown_checker = function(evt) { - var ref, stroke; - stroke = (ref = evt.which) != null ? ref : evt.keyCode; - this.search_field_scale(); - if (stroke !== 8 && this.pending_backstroke) { - this.clear_backstroke(); - } - switch (stroke) { - case 8: - this.backstroke_length = this.get_search_field_value().length; - break; - case 9: - if (this.results_showing && !this.is_multiple) { - this.result_select(evt); - } - this.mouse_on_container = false; - break; - case 13: - if (this.results_showing) { - evt.preventDefault(); - } - break; - case 27: - if (this.results_showing) { - evt.preventDefault(); - } - break; - case 32: - if (this.disable_search) { - evt.preventDefault(); - } - break; - case 38: - evt.preventDefault(); - this.keyup_arrow(); - break; - case 40: - evt.preventDefault(); - this.keydown_arrow(); - break; - } - }; - - AbstractChosen.prototype.keyup_checker = function(evt) { - var ref, stroke; - stroke = (ref = evt.which) != null ? ref : evt.keyCode; - this.search_field_scale(); - switch (stroke) { - case 8: - if (this.is_multiple && this.backstroke_length < 1 && this.choices_count() > 0) { - this.keydown_backstroke(); - } else if (!this.pending_backstroke) { - this.result_clear_highlight(); - this.results_search(); - } - break; - case 13: - evt.preventDefault(); - if (this.results_showing) { - this.result_select(evt); - } - break; - case 27: - if (this.results_showing) { - this.results_hide(); - } - break; - case 9: - case 16: - case 17: - case 18: - case 38: - case 40: - case 91: - break; - default: - this.results_search(); - break; - } - }; - - AbstractChosen.prototype.clipboard_event_checker = function(evt) { - if (this.is_disabled) { - return; - } - return setTimeout(((function(_this) { - return function() { - return _this.results_search(); - }; - })(this)), 50); - }; - - AbstractChosen.prototype.container_width = function() { - if (this.options.width != null) { - return this.options.width; - } else { - return this.form_field.offsetWidth + "px"; - } - }; - - AbstractChosen.prototype.include_option_in_results = function(option) { - if (this.is_multiple && (!this.display_selected_options && option.selected)) { - return false; - } - if (!this.display_disabled_options && option.disabled) { - return false; - } - if (option.empty) { - return false; - } - return true; - }; - - AbstractChosen.prototype.search_results_touchstart = function(evt) { - this.touch_started = true; - return this.search_results_mouseover(evt); - }; - - AbstractChosen.prototype.search_results_touchmove = function(evt) { - this.touch_started = false; - return this.search_results_mouseout(evt); - }; - - AbstractChosen.prototype.search_results_touchend = function(evt) { - if (this.touch_started) { - return this.search_results_mouseup(evt); - } - }; - - AbstractChosen.prototype.outerHTML = function(element) { - var tmp; - if (element.outerHTML) { - return element.outerHTML; - } - tmp = document.createElement("div"); - tmp.appendChild(element); - return tmp.innerHTML; - }; - - AbstractChosen.prototype.get_single_html = function() { - return "\n " + this.default_text + "\n
          \n
          \n
          \n
          \n \n
          \n
            \n
            "; - }; - - AbstractChosen.prototype.get_multi_html = function() { - return "
              \n
            • \n \n
            • \n
            \n
            \n
              \n
              "; - }; - - AbstractChosen.prototype.get_no_results_html = function(terms) { - return "
            • \n " + this.results_none_found + " " + (this.escape_html(terms)) + "\n
            • "; - }; - - AbstractChosen.browser_is_supported = function() { - if ("Microsoft Internet Explorer" === window.navigator.appName) { - return document.documentMode >= 8; - } - if (/iP(od|hone)/i.test(window.navigator.userAgent) || /IEMobile/i.test(window.navigator.userAgent) || /Windows Phone/i.test(window.navigator.userAgent) || /BlackBerry/i.test(window.navigator.userAgent) || /BB10/i.test(window.navigator.userAgent) || /Android.*Mobile/i.test(window.navigator.userAgent)) { - return false; - } - return true; - }; - - AbstractChosen.default_multiple_text = "Select Some Options"; - - AbstractChosen.default_single_text = "Select an Option"; - - AbstractChosen.default_no_result_text = "No results match"; - - return AbstractChosen; - - })(); - - this.Chosen = (function(superClass) { - var triggerHtmlEvent; - - extend(Chosen, superClass); - - function Chosen() { - return Chosen.__super__.constructor.apply(this, arguments); - } - - Chosen.prototype.setup = function() { - return this.current_selectedIndex = this.form_field.selectedIndex; - }; - - Chosen.prototype.set_up_html = function() { - var container_classes, container_props; - container_classes = ["chosen-container"]; - container_classes.push("chosen-container-" + (this.is_multiple ? "multi" : "single")); - if (this.inherit_select_classes && this.form_field.className) { - container_classes.push(this.form_field.className); - } - if (this.is_rtl) { - container_classes.push("chosen-rtl"); - } - container_props = { - 'class': container_classes.join(' '), - 'title': this.form_field.title - }; - if (this.form_field.id.length) { - container_props.id = this.form_field.id.replace(/[^\w]/g, '_') + "_chosen"; - } - this.container = new Element('div', container_props); - this.container.setStyle({ - width: this.container_width() - }); - if (this.is_multiple) { - this.container.update(this.get_multi_html()); - } else { - this.container.update(this.get_single_html()); - } - this.form_field.hide().insert({ - after: this.container - }); - this.dropdown = this.container.down('div.chosen-drop'); - this.search_field = this.container.down('input'); - this.search_results = this.container.down('ul.chosen-results'); - this.search_field_scale(); - this.search_no_results = this.container.down('li.no-results'); - if (this.is_multiple) { - this.search_choices = this.container.down('ul.chosen-choices'); - this.search_container = this.container.down('li.search-field'); - } else { - this.search_container = this.container.down('div.chosen-search'); - this.selected_item = this.container.down('.chosen-single'); - } - this.results_build(); - this.set_tab_index(); - return this.set_label_behavior(); - }; - - Chosen.prototype.on_ready = function() { - return this.form_field.fire("chosen:ready", { - chosen: this - }); - }; - - Chosen.prototype.register_observers = function() { - this.container.observe("touchstart", (function(_this) { - return function(evt) { - return _this.container_mousedown(evt); - }; - })(this)); - this.container.observe("touchend", (function(_this) { - return function(evt) { - return _this.container_mouseup(evt); - }; - })(this)); - this.container.observe("mousedown", (function(_this) { - return function(evt) { - return _this.container_mousedown(evt); - }; - })(this)); - this.container.observe("mouseup", (function(_this) { - return function(evt) { - return _this.container_mouseup(evt); - }; - })(this)); - this.container.observe("mouseenter", (function(_this) { - return function(evt) { - return _this.mouse_enter(evt); - }; - })(this)); - this.container.observe("mouseleave", (function(_this) { - return function(evt) { - return _this.mouse_leave(evt); - }; - })(this)); - this.search_results.observe("mouseup", (function(_this) { - return function(evt) { - return _this.search_results_mouseup(evt); - }; - })(this)); - this.search_results.observe("mouseover", (function(_this) { - return function(evt) { - return _this.search_results_mouseover(evt); - }; - })(this)); - this.search_results.observe("mouseout", (function(_this) { - return function(evt) { - return _this.search_results_mouseout(evt); - }; - })(this)); - this.search_results.observe("mousewheel", (function(_this) { - return function(evt) { - return _this.search_results_mousewheel(evt); - }; - })(this)); - this.search_results.observe("DOMMouseScroll", (function(_this) { - return function(evt) { - return _this.search_results_mousewheel(evt); - }; - })(this)); - this.search_results.observe("touchstart", (function(_this) { - return function(evt) { - return _this.search_results_touchstart(evt); - }; - })(this)); - this.search_results.observe("touchmove", (function(_this) { - return function(evt) { - return _this.search_results_touchmove(evt); - }; - })(this)); - this.search_results.observe("touchend", (function(_this) { - return function(evt) { - return _this.search_results_touchend(evt); - }; - })(this)); - this.form_field.observe("chosen:updated", (function(_this) { - return function(evt) { - return _this.results_update_field(evt); - }; - })(this)); - this.form_field.observe("chosen:activate", (function(_this) { - return function(evt) { - return _this.activate_field(evt); - }; - })(this)); - this.form_field.observe("chosen:open", (function(_this) { - return function(evt) { - return _this.container_mousedown(evt); - }; - })(this)); - this.form_field.observe("chosen:close", (function(_this) { - return function(evt) { - return _this.close_field(evt); - }; - })(this)); - this.search_field.observe("blur", (function(_this) { - return function(evt) { - return _this.input_blur(evt); - }; - })(this)); - this.search_field.observe("keyup", (function(_this) { - return function(evt) { - return _this.keyup_checker(evt); - }; - })(this)); - this.search_field.observe("keydown", (function(_this) { - return function(evt) { - return _this.keydown_checker(evt); - }; - })(this)); - this.search_field.observe("focus", (function(_this) { - return function(evt) { - return _this.input_focus(evt); - }; - })(this)); - this.search_field.observe("cut", (function(_this) { - return function(evt) { - return _this.clipboard_event_checker(evt); - }; - })(this)); - this.search_field.observe("paste", (function(_this) { - return function(evt) { - return _this.clipboard_event_checker(evt); - }; - })(this)); - if (this.is_multiple) { - return this.search_choices.observe("click", (function(_this) { - return function(evt) { - return _this.choices_click(evt); - }; - })(this)); - } else { - return this.container.observe("click", (function(_this) { - return function(evt) { - return evt.preventDefault(); - }; - })(this)); - } - }; - - Chosen.prototype.destroy = function() { - var event, i, len, ref; - this.container.ownerDocument.stopObserving("click", this.click_test_action); - ref = ['chosen:updated', 'chosen:activate', 'chosen:open', 'chosen:close']; - for (i = 0, len = ref.length; i < len; i++) { - event = ref[i]; - this.form_field.stopObserving(event); - } - this.container.stopObserving(); - this.search_results.stopObserving(); - this.search_field.stopObserving(); - if (this.form_field_label != null) { - this.form_field_label.stopObserving(); - } - if (this.is_multiple) { - this.search_choices.stopObserving(); - this.container.select(".search-choice-close").each(function(choice) { - return choice.stopObserving(); - }); - } else { - this.selected_item.stopObserving(); - } - if (this.search_field.tabIndex) { - this.form_field.tabIndex = this.search_field.tabIndex; - } - this.container.remove(); - return this.form_field.show(); - }; - - Chosen.prototype.search_field_disabled = function() { - var ref; - this.is_disabled = this.form_field.disabled || ((ref = this.form_field.up('fieldset')) != null ? ref.disabled : void 0) || false; - if (this.is_disabled) { - this.container.addClassName('chosen-disabled'); - } else { - this.container.removeClassName('chosen-disabled'); - } - this.search_field.disabled = this.is_disabled; - if (!this.is_multiple) { - this.selected_item.stopObserving('focus', this.activate_field); - } - if (this.is_disabled) { - return this.close_field(); - } else if (!this.is_multiple) { - return this.selected_item.observe('focus', this.activate_field); - } - }; - - Chosen.prototype.container_mousedown = function(evt) { - var ref; - if (this.is_disabled) { - return; - } - if (evt && ((ref = evt.type) === 'mousedown' || ref === 'touchstart') && !this.results_showing) { - evt.preventDefault(); - } - if (!((evt != null) && evt.target.hasClassName("search-choice-close"))) { - if (!this.active_field) { - if (this.is_multiple) { - this.search_field.clear(); - } - this.container.ownerDocument.observe("click", this.click_test_action); - this.results_show(); - } else if (!this.is_multiple && evt && (evt.target === this.selected_item || evt.target.up("a.chosen-single"))) { - this.results_toggle(); - } - return this.activate_field(); - } - }; - - Chosen.prototype.container_mouseup = function(evt) { - if (evt.target.nodeName === "ABBR" && !this.is_disabled) { - return this.results_reset(evt); - } - }; - - Chosen.prototype.search_results_mousewheel = function(evt) { - var delta; - delta = evt.deltaY || -evt.wheelDelta || evt.detail; - if (delta != null) { - evt.preventDefault(); - if (evt.type === 'DOMMouseScroll') { - delta = delta * 40; - } - return this.search_results.scrollTop = delta + this.search_results.scrollTop; - } - }; - - Chosen.prototype.blur_test = function(evt) { - if (!this.active_field && this.container.hasClassName("chosen-container-active")) { - return this.close_field(); - } - }; - - Chosen.prototype.close_field = function() { - this.container.ownerDocument.stopObserving("click", this.click_test_action); - this.active_field = false; - this.results_hide(); - this.container.removeClassName("chosen-container-active"); - this.clear_backstroke(); - this.show_search_field_default(); - this.search_field_scale(); - return this.search_field.blur(); - }; - - Chosen.prototype.activate_field = function() { - if (this.is_disabled) { - return; - } - this.container.addClassName("chosen-container-active"); - this.active_field = true; - this.search_field.value = this.get_search_field_value(); - return this.search_field.focus(); - }; - - Chosen.prototype.test_active_click = function(evt) { - if (evt.target.up('.chosen-container') === this.container) { - return this.active_field = true; - } else { - return this.close_field(); - } - }; - - Chosen.prototype.results_build = function() { - this.parsing = true; - this.selected_option_count = null; - this.results_data = SelectParser.select_to_array(this.form_field); - if (this.is_multiple) { - this.search_choices.select("li.search-choice").invoke("remove"); - } else if (!this.is_multiple) { - this.single_set_selected_text(); - if (this.disable_search || this.form_field.options.length <= this.disable_search_threshold) { - this.search_field.readOnly = true; - this.container.addClassName("chosen-container-single-nosearch"); - } else { - this.search_field.readOnly = false; - this.container.removeClassName("chosen-container-single-nosearch"); - } - } - this.update_results_content(this.results_option_build({ - first: true - })); - this.search_field_disabled(); - this.show_search_field_default(); - this.search_field_scale(); - return this.parsing = false; - }; - - Chosen.prototype.result_do_highlight = function(el) { - var high_bottom, high_top, maxHeight, visible_bottom, visible_top; - this.result_clear_highlight(); - this.result_highlight = el; - this.result_highlight.addClassName("highlighted"); - maxHeight = parseInt(this.search_results.getStyle('maxHeight'), 10); - visible_top = this.search_results.scrollTop; - visible_bottom = maxHeight + visible_top; - high_top = this.result_highlight.positionedOffset().top; - high_bottom = high_top + this.result_highlight.getHeight(); - if (high_bottom >= visible_bottom) { - return this.search_results.scrollTop = (high_bottom - maxHeight) > 0 ? high_bottom - maxHeight : 0; - } else if (high_top < visible_top) { - return this.search_results.scrollTop = high_top; - } - }; - - Chosen.prototype.result_clear_highlight = function() { - if (this.result_highlight) { - this.result_highlight.removeClassName('highlighted'); - } - return this.result_highlight = null; - }; - - Chosen.prototype.results_show = function() { - if (this.is_multiple && this.max_selected_options <= this.choices_count()) { - this.form_field.fire("chosen:maxselected", { - chosen: this - }); - return false; - } - this.container.addClassName("chosen-with-drop"); - this.results_showing = true; - this.search_field.focus(); - this.search_field.value = this.get_search_field_value(); - this.winnow_results(); - return this.form_field.fire("chosen:showing_dropdown", { - chosen: this - }); - }; - - Chosen.prototype.update_results_content = function(content) { - return this.search_results.update(content); - }; - - Chosen.prototype.results_hide = function() { - if (this.results_showing) { - this.result_clear_highlight(); - this.container.removeClassName("chosen-with-drop"); - this.form_field.fire("chosen:hiding_dropdown", { - chosen: this - }); - } - return this.results_showing = false; - }; - - Chosen.prototype.set_tab_index = function(el) { - var ti; - if (this.form_field.tabIndex) { - ti = this.form_field.tabIndex; - this.form_field.tabIndex = -1; - return this.search_field.tabIndex = ti; - } - }; - - Chosen.prototype.set_label_behavior = function() { - this.form_field_label = this.form_field.up("label"); - if (this.form_field_label == null) { - this.form_field_label = $$("label[for='" + this.form_field.id + "']").first(); - } - if (this.form_field_label != null) { - return this.form_field_label.observe("click", this.label_click_handler); - } - }; - - Chosen.prototype.show_search_field_default = function() { - if (this.is_multiple && this.choices_count() < 1 && !this.active_field) { - this.search_field.value = this.default_text; - return this.search_field.addClassName("default"); - } else { - this.search_field.value = ""; - return this.search_field.removeClassName("default"); - } - }; - - Chosen.prototype.search_results_mouseup = function(evt) { - var target; - target = evt.target.hasClassName("active-result") ? evt.target : evt.target.up(".active-result"); - if (target) { - this.result_highlight = target; - this.result_select(evt); - return this.search_field.focus(); - } - }; - - Chosen.prototype.search_results_mouseover = function(evt) { - var target; - target = evt.target.hasClassName("active-result") ? evt.target : evt.target.up(".active-result"); - if (target) { - return this.result_do_highlight(target); - } - }; - - Chosen.prototype.search_results_mouseout = function(evt) { - if (evt.target.hasClassName('active-result') || evt.target.up('.active-result')) { - return this.result_clear_highlight(); - } - }; - - Chosen.prototype.choice_build = function(item) { - var choice, close_link; - choice = new Element('li', { - "class": "search-choice" - }).update("" + (this.choice_label(item)) + ""); - if (item.disabled) { - choice.addClassName('search-choice-disabled'); - } else { - close_link = new Element('a', { - href: '#', - "class": 'search-choice-close', - rel: item.array_index - }); - close_link.observe("click", (function(_this) { - return function(evt) { - return _this.choice_destroy_link_click(evt); - }; - })(this)); - choice.insert(close_link); - } - return this.search_container.insert({ - before: choice - }); - }; - - Chosen.prototype.choice_destroy_link_click = function(evt) { - evt.preventDefault(); - evt.stopPropagation(); - if (!this.is_disabled) { - return this.choice_destroy(evt.target); - } - }; - - Chosen.prototype.choice_destroy = function(link) { - if (this.result_deselect(link.readAttribute("rel"))) { - if (this.active_field) { - this.search_field.focus(); - } else { - this.show_search_field_default(); - } - if (this.is_multiple && this.choices_count() > 0 && this.get_search_field_value().length < 1) { - this.results_hide(); - } - link.up('li').remove(); - return this.search_field_scale(); - } - }; - - Chosen.prototype.results_reset = function() { - this.reset_single_select_options(); - this.form_field.options[0].selected = true; - this.single_set_selected_text(); - this.show_search_field_default(); - this.results_reset_cleanup(); - this.trigger_form_field_change(); - if (this.active_field) { - return this.results_hide(); - } - }; - - Chosen.prototype.results_reset_cleanup = function() { - var deselect_trigger; - this.current_selectedIndex = this.form_field.selectedIndex; - deselect_trigger = this.selected_item.down("abbr"); - if (deselect_trigger) { - return deselect_trigger.remove(); - } - }; - - Chosen.prototype.result_select = function(evt) { - var high, item; - if (this.result_highlight) { - high = this.result_highlight; - this.result_clear_highlight(); - if (this.is_multiple && this.max_selected_options <= this.choices_count()) { - this.form_field.fire("chosen:maxselected", { - chosen: this - }); - return false; - } - if (this.is_multiple) { - high.removeClassName("active-result"); - } else { - this.reset_single_select_options(); - } - high.addClassName("result-selected"); - item = this.results_data[high.getAttribute("data-option-array-index")]; - item.selected = true; - this.form_field.options[item.options_index].selected = true; - this.selected_option_count = null; - this.search_field.value = ""; - if (this.is_multiple) { - this.choice_build(item); - } else { - this.single_set_selected_text(this.choice_label(item)); - } - if (this.is_multiple && (!this.hide_results_on_select || (evt.metaKey || evt.ctrlKey))) { - this.winnow_results(); - } else { - this.results_hide(); - this.show_search_field_default(); - } - if (this.is_multiple || this.form_field.selectedIndex !== this.current_selectedIndex) { - this.trigger_form_field_change(); - } - this.current_selectedIndex = this.form_field.selectedIndex; - evt.preventDefault(); - return this.search_field_scale(); - } - }; - - Chosen.prototype.single_set_selected_text = function(text) { - if (text == null) { - text = this.default_text; - } - if (text === this.default_text) { - this.selected_item.addClassName("chosen-default"); - } else { - this.single_deselect_control_build(); - this.selected_item.removeClassName("chosen-default"); - } - return this.selected_item.down("span").update(text); - }; - - Chosen.prototype.result_deselect = function(pos) { - var result_data; - result_data = this.results_data[pos]; - if (!this.form_field.options[result_data.options_index].disabled) { - result_data.selected = false; - this.form_field.options[result_data.options_index].selected = false; - this.selected_option_count = null; - this.result_clear_highlight(); - if (this.results_showing) { - this.winnow_results(); - } - this.trigger_form_field_change(); - this.search_field_scale(); - return true; - } else { - return false; - } - }; - - Chosen.prototype.single_deselect_control_build = function() { - if (!this.allow_single_deselect) { - return; - } - if (!this.selected_item.down("abbr")) { - this.selected_item.down("span").insert({ - after: "" - }); - } - return this.selected_item.addClassName("chosen-single-with-deselect"); - }; - - Chosen.prototype.get_search_field_value = function() { - return this.search_field.value; - }; - - Chosen.prototype.get_search_text = function() { - return this.get_search_field_value().strip(); - }; - - Chosen.prototype.escape_html = function(text) { - return text.escapeHTML(); - }; - - Chosen.prototype.winnow_results_set_highlight = function() { - var do_high; - if (!this.is_multiple) { - do_high = this.search_results.down(".result-selected.active-result"); - } - if (do_high == null) { - do_high = this.search_results.down(".active-result"); - } - if (do_high != null) { - return this.result_do_highlight(do_high); - } - }; - - Chosen.prototype.no_results = function(terms) { - this.search_results.insert(this.get_no_results_html(terms)); - return this.form_field.fire("chosen:no_results", { - chosen: this - }); - }; - - Chosen.prototype.no_results_clear = function() { - var nr, results1; - nr = null; - results1 = []; - while (nr = this.search_results.down(".no-results")) { - results1.push(nr.remove()); - } - return results1; - }; - - Chosen.prototype.keydown_arrow = function() { - var next_sib; - if (this.results_showing && this.result_highlight) { - next_sib = this.result_highlight.next('.active-result'); - if (next_sib) { - return this.result_do_highlight(next_sib); - } - } else { - return this.results_show(); - } - }; - - Chosen.prototype.keyup_arrow = function() { - var actives, prevs, sibs; - if (!this.results_showing && !this.is_multiple) { - return this.results_show(); - } else if (this.result_highlight) { - sibs = this.result_highlight.previousSiblings(); - actives = this.search_results.select("li.active-result"); - prevs = sibs.intersect(actives); - if (prevs.length) { - return this.result_do_highlight(prevs.first()); - } else { - if (this.choices_count() > 0) { - this.results_hide(); - } - return this.result_clear_highlight(); - } - } - }; - - Chosen.prototype.keydown_backstroke = function() { - var next_available_destroy; - if (this.pending_backstroke) { - this.choice_destroy(this.pending_backstroke.down("a")); - return this.clear_backstroke(); - } else { - next_available_destroy = this.search_container.siblings().last(); - if (next_available_destroy && next_available_destroy.hasClassName("search-choice") && !next_available_destroy.hasClassName("search-choice-disabled")) { - this.pending_backstroke = next_available_destroy; - if (this.pending_backstroke) { - this.pending_backstroke.addClassName("search-choice-focus"); - } - if (this.single_backstroke_delete) { - return this.keydown_backstroke(); - } else { - return this.pending_backstroke.addClassName("search-choice-focus"); - } - } - } - }; - - Chosen.prototype.clear_backstroke = function() { - if (this.pending_backstroke) { - this.pending_backstroke.removeClassName("search-choice-focus"); - } - return this.pending_backstroke = null; - }; - - Chosen.prototype.search_field_scale = function() { - var container_width, div, i, len, style, style_block, styles, width; - if (!this.is_multiple) { - return; - } - style_block = { - position: 'absolute', - left: '-1000px', - top: '-1000px', - display: 'none', - whiteSpace: 'pre' - }; - styles = ['fontSize', 'fontStyle', 'fontWeight', 'fontFamily', 'lineHeight', 'textTransform', 'letterSpacing']; - for (i = 0, len = styles.length; i < len; i++) { - style = styles[i]; - style_block[style] = this.search_field.getStyle(style); - } - div = new Element('div').update(this.escape_html(this.get_search_field_value())); - div.setStyle(style_block); - document.body.appendChild(div); - width = div.measure('width') + 25; - div.remove(); - if (container_width = this.container.getWidth()) { - width = Math.min(container_width - 10, width); - } - return this.search_field.setStyle({ - width: width + 'px' - }); - }; - - Chosen.prototype.trigger_form_field_change = function() { - triggerHtmlEvent(this.form_field, 'input'); - return triggerHtmlEvent(this.form_field, 'change'); - }; - - triggerHtmlEvent = function(element, eventType) { - var error, evt; - if (element.dispatchEvent) { - try { - evt = new Event(eventType, { - bubbles: true, - cancelable: true - }); - } catch (error) { - evt = document.createEvent('HTMLEvents'); - evt.initEvent(eventType, true, true); - } - return element.dispatchEvent(evt); - } else { - return element.fireEvent("on" + eventType, document.createEventObject()); - } - }; - - return Chosen; - - })(AbstractChosen); - -}).call(this); diff --git a/web/skins/classic/js/chosen/chosen.proto.min.js b/web/skins/classic/js/chosen/chosen.proto.min.js deleted file mode 100644 index f299bc21b..000000000 --- a/web/skins/classic/js/chosen/chosen.proto.min.js +++ /dev/null @@ -1,3 +0,0 @@ -/* Chosen v1.8.2 | (c) 2011-2017 by Harvest | MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md */ - -(function(){var e,t,s=function(e,t){return function(){return e.apply(t,arguments)}},i=function(e,t){function s(){this.constructor=e}for(var i in t)r.call(t,i)&&(e[i]=t[i]);return s.prototype=t.prototype,e.prototype=new s,e.__super__=t.prototype,e},r={}.hasOwnProperty;(t=function(){function e(){this.options_index=0,this.parsed=[]}return e.prototype.add_node=function(e){return"OPTGROUP"===e.nodeName.toUpperCase()?this.add_group(e):this.add_option(e)},e.prototype.add_group=function(e){var t,s,i,r,n,o;for(t=this.parsed.length,this.parsed.push({array_index:t,group:!0,label:e.label,title:e.title?e.title:void 0,children:0,disabled:e.disabled,classes:e.className}),o=[],s=0,i=(n=e.childNodes).length;s"+e.group_label+""+e.html:e.html},e.prototype.mouse_enter=function(){return this.mouse_on_container=!0},e.prototype.mouse_leave=function(){return this.mouse_on_container=!1},e.prototype.input_focus=function(e){if(this.is_multiple){if(!this.active_field)return setTimeout(function(e){return function(){return e.container_mousedown()}}(this),50)}else if(!this.active_field)return this.activate_field()},e.prototype.input_blur=function(e){if(!this.mouse_on_container)return this.active_field=!1,setTimeout(function(e){return function(){return e.blur_test()}}(this),100)},e.prototype.label_click_handler=function(e){return this.is_multiple?this.container_mousedown(e):this.activate_field()},e.prototype.results_option_build=function(e){var t,s,i,r,n,o,l;for(t="",l=0,r=0,n=(o=this.results_data).length;r=this.max_shown_results));r++);return t},e.prototype.result_add_option=function(e){var t,s;return e.search_match&&this.include_option_in_results(e)?(t=[],e.disabled||e.selected&&this.is_multiple||t.push("active-result"),!e.disabled||e.selected&&this.is_multiple||t.push("disabled-result"),e.selected&&t.push("result-selected"),null!=e.group_array_index&&t.push("group-option"),""!==e.classes&&t.push(e.classes),s=document.createElement("li"),s.className=t.join(" "),s.style.cssText=e.style,s.setAttribute("data-option-array-index",e.array_index),s.innerHTML=e.highlighted_html||e.html,e.title&&(s.title=e.title),this.outerHTML(s)):""},e.prototype.result_add_group=function(e){var t,s;return(e.search_match||e.group_match)&&e.active_options>0?((t=[]).push("group-result"),e.classes&&t.push(e.classes),s=document.createElement("li"),s.className=t.join(" "),s.innerHTML=e.highlighted_html||this.escape_html(e.label),e.title&&(s.title=e.title),this.outerHTML(s)):""},e.prototype.results_update_field=function(){if(this.set_default_text(),this.is_multiple||this.results_reset_cleanup(),this.result_clear_highlight(),this.results_build(),this.results_showing)return this.winnow_results()},e.prototype.reset_single_select_options=function(){var e,t,s,i,r;for(r=[],e=0,t=(s=this.results_data).length;e"+this.escape_html(t)+""+this.escape_html(d)),null!=a&&(a.group_match=!0)):null!=r.group_array_index&&this.results_data[r.group_array_index].search_match&&(r.search_match=!0)));return this.result_clear_highlight(),c<1&&o.length?(this.update_results_content(""),this.no_results(o)):(this.update_results_content(this.results_option_build()),this.winnow_results_set_highlight())},e.prototype.get_search_regex=function(e){var t,s;return s=this.search_contains?e:"(^|\\s|\\b)"+e+"[^\\s]*",this.enable_split_word_search||this.search_contains||(s="^"+s),t=this.case_sensitive_search?"":"i",new RegExp(s,t)},e.prototype.search_string_match=function(e,t){var s;return s=t.exec(e),!this.search_contains&&(null!=s?s[1]:void 0)&&(s.index+=1),s},e.prototype.choices_count=function(){var e,t,s;if(null!=this.selected_option_count)return this.selected_option_count;for(this.selected_option_count=0,e=0,t=(s=this.form_field.options).length;e0?this.keydown_backstroke():this.pending_backstroke||(this.result_clear_highlight(),this.results_search());break;case 13:e.preventDefault(),this.results_showing&&this.result_select(e);break;case 27:this.results_showing&&this.results_hide();break;case 9:case 16:case 17:case 18:case 38:case 40:case 91:break;default:this.results_search()}},e.prototype.clipboard_event_checker=function(e){if(!this.is_disabled)return setTimeout(function(e){return function(){return e.results_search()}}(this),50)},e.prototype.container_width=function(){return null!=this.options.width?this.options.width:this.form_field.offsetWidth+"px"},e.prototype.include_option_in_results=function(e){return!(this.is_multiple&&!this.display_selected_options&&e.selected)&&(!(!this.display_disabled_options&&e.disabled)&&!e.empty)},e.prototype.search_results_touchstart=function(e){return this.touch_started=!0,this.search_results_mouseover(e)},e.prototype.search_results_touchmove=function(e){return this.touch_started=!1,this.search_results_mouseout(e)},e.prototype.search_results_touchend=function(e){if(this.touch_started)return this.search_results_mouseup(e)},e.prototype.outerHTML=function(e){var t;return e.outerHTML?e.outerHTML:((t=document.createElement("div")).appendChild(e),t.innerHTML)},e.prototype.get_single_html=function(){return'\n '+this.default_text+'\n
              \n
              \n
              \n \n
                \n
                '},e.prototype.get_multi_html=function(){return'
                  \n
                • \n \n
                • \n
                \n
                \n
                  \n
                  '},e.prototype.get_no_results_html=function(e){return'
                • \n '+this.results_none_found+" "+this.escape_html(e)+"\n
                • "},e.browser_is_supported=function(){return"Microsoft Internet Explorer"===window.navigator.appName?document.documentMode>=8:!(/iP(od|hone)/i.test(window.navigator.userAgent)||/IEMobile/i.test(window.navigator.userAgent)||/Windows Phone/i.test(window.navigator.userAgent)||/BlackBerry/i.test(window.navigator.userAgent)||/BB10/i.test(window.navigator.userAgent)||/Android.*Mobile/i.test(window.navigator.userAgent))},e.default_multiple_text="Select Some Options",e.default_single_text="Select an Option",e.default_no_result_text="No results match",e}(),this.Chosen=function(s){function r(){return r.__super__.constructor.apply(this,arguments)}var n;return i(r,e),r.prototype.setup=function(){return this.current_selectedIndex=this.form_field.selectedIndex},r.prototype.set_up_html=function(){var e,t;return(e=["chosen-container"]).push("chosen-container-"+(this.is_multiple?"multi":"single")),this.inherit_select_classes&&this.form_field.className&&e.push(this.form_field.className),this.is_rtl&&e.push("chosen-rtl"),t={class:e.join(" "),title:this.form_field.title},this.form_field.id.length&&(t.id=this.form_field.id.replace(/[^\w]/g,"_")+"_chosen"),this.container=new Element("div",t),this.container.setStyle({width:this.container_width()}),this.is_multiple?this.container.update(this.get_multi_html()):this.container.update(this.get_single_html()),this.form_field.hide().insert({after:this.container}),this.dropdown=this.container.down("div.chosen-drop"),this.search_field=this.container.down("input"),this.search_results=this.container.down("ul.chosen-results"),this.search_field_scale(),this.search_no_results=this.container.down("li.no-results"),this.is_multiple?(this.search_choices=this.container.down("ul.chosen-choices"),this.search_container=this.container.down("li.search-field")):(this.search_container=this.container.down("div.chosen-search"),this.selected_item=this.container.down(".chosen-single")),this.results_build(),this.set_tab_index(),this.set_label_behavior()},r.prototype.on_ready=function(){return this.form_field.fire("chosen:ready",{chosen:this})},r.prototype.register_observers=function(){return this.container.observe("touchstart",function(e){return function(t){return e.container_mousedown(t)}}(this)),this.container.observe("touchend",function(e){return function(t){return e.container_mouseup(t)}}(this)),this.container.observe("mousedown",function(e){return function(t){return e.container_mousedown(t)}}(this)),this.container.observe("mouseup",function(e){return function(t){return e.container_mouseup(t)}}(this)),this.container.observe("mouseenter",function(e){return function(t){return e.mouse_enter(t)}}(this)),this.container.observe("mouseleave",function(e){return function(t){return e.mouse_leave(t)}}(this)),this.search_results.observe("mouseup",function(e){return function(t){return e.search_results_mouseup(t)}}(this)),this.search_results.observe("mouseover",function(e){return function(t){return e.search_results_mouseover(t)}}(this)),this.search_results.observe("mouseout",function(e){return function(t){return e.search_results_mouseout(t)}}(this)),this.search_results.observe("mousewheel",function(e){return function(t){return e.search_results_mousewheel(t)}}(this)),this.search_results.observe("DOMMouseScroll",function(e){return function(t){return e.search_results_mousewheel(t)}}(this)),this.search_results.observe("touchstart",function(e){return function(t){return e.search_results_touchstart(t)}}(this)),this.search_results.observe("touchmove",function(e){return function(t){return e.search_results_touchmove(t)}}(this)),this.search_results.observe("touchend",function(e){return function(t){return e.search_results_touchend(t)}}(this)),this.form_field.observe("chosen:updated",function(e){return function(t){return e.results_update_field(t)}}(this)),this.form_field.observe("chosen:activate",function(e){return function(t){return e.activate_field(t)}}(this)),this.form_field.observe("chosen:open",function(e){return function(t){return e.container_mousedown(t)}}(this)),this.form_field.observe("chosen:close",function(e){return function(t){return e.close_field(t)}}(this)),this.search_field.observe("blur",function(e){return function(t){return e.input_blur(t)}}(this)),this.search_field.observe("keyup",function(e){return function(t){return e.keyup_checker(t)}}(this)),this.search_field.observe("keydown",function(e){return function(t){return e.keydown_checker(t)}}(this)),this.search_field.observe("focus",function(e){return function(t){return e.input_focus(t)}}(this)),this.search_field.observe("cut",function(e){return function(t){return e.clipboard_event_checker(t)}}(this)),this.search_field.observe("paste",function(e){return function(t){return e.clipboard_event_checker(t)}}(this)),this.is_multiple?this.search_choices.observe("click",function(e){return function(t){return e.choices_click(t)}}(this)):this.container.observe("click",function(e){return e.preventDefault()})},r.prototype.destroy=function(){var e,t,s,i;for(this.container.ownerDocument.stopObserving("click",this.click_test_action),t=0,s=(i=["chosen:updated","chosen:activate","chosen:open","chosen:close"]).length;t=r?this.search_results.scrollTop=t-i>0?t-i:0:s"+this.choice_label(e)+""),e.disabled?t.addClassName("search-choice-disabled"):((s=new Element("a",{href:"#",class:"search-choice-close",rel:e.array_index})).observe("click",function(e){return function(t){return e.choice_destroy_link_click(t)}}(this)),t.insert(s)),this.search_container.insert({before:t})},r.prototype.choice_destroy_link_click=function(e){if(e.preventDefault(),e.stopPropagation(),!this.is_disabled)return this.choice_destroy(e.target)},r.prototype.choice_destroy=function(e){if(this.result_deselect(e.readAttribute("rel")))return this.active_field?this.search_field.focus():this.show_search_field_default(),this.is_multiple&&this.choices_count()>0&&this.get_search_field_value().length<1&&this.results_hide(),e.up("li").remove(),this.search_field_scale()},r.prototype.results_reset=function(){if(this.reset_single_select_options(),this.form_field.options[0].selected=!0,this.single_set_selected_text(),this.show_search_field_default(),this.results_reset_cleanup(),this.trigger_form_field_change(),this.active_field)return this.results_hide()},r.prototype.results_reset_cleanup=function(){var e;if(this.current_selectedIndex=this.form_field.selectedIndex,e=this.selected_item.down("abbr"))return e.remove()},r.prototype.result_select=function(e){var t,s;if(this.result_highlight)return t=this.result_highlight,this.result_clear_highlight(),this.is_multiple&&this.max_selected_options<=this.choices_count()?(this.form_field.fire("chosen:maxselected",{chosen:this}),!1):(this.is_multiple?t.removeClassName("active-result"):this.reset_single_select_options(),t.addClassName("result-selected"),s=this.results_data[t.getAttribute("data-option-array-index")],s.selected=!0,this.form_field.options[s.options_index].selected=!0,this.selected_option_count=null,this.search_field.value="",this.is_multiple?this.choice_build(s):this.single_set_selected_text(this.choice_label(s)),this.is_multiple&&(!this.hide_results_on_select||e.metaKey||e.ctrlKey)?this.winnow_results():(this.results_hide(),this.show_search_field_default()),(this.is_multiple||this.form_field.selectedIndex!==this.current_selectedIndex)&&this.trigger_form_field_change(),this.current_selectedIndex=this.form_field.selectedIndex,e.preventDefault(),this.search_field_scale())},r.prototype.single_set_selected_text=function(e){return null==e&&(e=this.default_text),e===this.default_text?this.selected_item.addClassName("chosen-default"):(this.single_deselect_control_build(),this.selected_item.removeClassName("chosen-default")),this.selected_item.down("span").update(e)},r.prototype.result_deselect=function(e){var t;return t=this.results_data[e],!this.form_field.options[t.options_index].disabled&&(t.selected=!1,this.form_field.options[t.options_index].selected=!1,this.selected_option_count=null,this.result_clear_highlight(),this.results_showing&&this.winnow_results(),this.trigger_form_field_change(),this.search_field_scale(),!0)},r.prototype.single_deselect_control_build=function(){if(this.allow_single_deselect)return this.selected_item.down("abbr")||this.selected_item.down("span").insert({after:''}),this.selected_item.addClassName("chosen-single-with-deselect")},r.prototype.get_search_field_value=function(){return this.search_field.value},r.prototype.get_search_text=function(){return this.get_search_field_value().strip()},r.prototype.escape_html=function(e){return e.escapeHTML()},r.prototype.winnow_results_set_highlight=function(){var e;if(this.is_multiple||(e=this.search_results.down(".result-selected.active-result")),null==e&&(e=this.search_results.down(".active-result")),null!=e)return this.result_do_highlight(e)},r.prototype.no_results=function(e){return this.search_results.insert(this.get_no_results_html(e)),this.form_field.fire("chosen:no_results",{chosen:this})},r.prototype.no_results_clear=function(){var e,t;for(e=null,t=[];e=this.search_results.down(".no-results");)t.push(e.remove());return t},r.prototype.keydown_arrow=function(){var e;return this.results_showing&&this.result_highlight?(e=this.result_highlight.next(".active-result"))?this.result_do_highlight(e):void 0:this.results_show()},r.prototype.keyup_arrow=function(){var e,t,s;return this.results_showing||this.is_multiple?this.result_highlight?(s=this.result_highlight.previousSiblings(),e=this.search_results.select("li.active-result"),(t=s.intersect(e)).length?this.result_do_highlight(t.first()):(this.choices_count()>0&&this.results_hide(),this.result_clear_highlight())):void 0:this.results_show()},r.prototype.keydown_backstroke=function(){var e;return this.pending_backstroke?(this.choice_destroy(this.pending_backstroke.down("a")),this.clear_backstroke()):(e=this.search_container.siblings().last())&&e.hasClassName("search-choice")&&!e.hasClassName("search-choice-disabled")?(this.pending_backstroke=e,this.pending_backstroke&&this.pending_backstroke.addClassName("search-choice-focus"),this.single_backstroke_delete?this.keydown_backstroke():this.pending_backstroke.addClassName("search-choice-focus")):void 0},r.prototype.clear_backstroke=function(){return this.pending_backstroke&&this.pending_backstroke.removeClassName("search-choice-focus"),this.pending_backstroke=null},r.prototype.search_field_scale=function(){var e,t,s,i,r,n,o,l;if(this.is_multiple){for(n={position:"absolute",left:"-1000px",top:"-1000px",display:"none",whiteSpace:"pre"},s=0,i=(o=["fontSize","fontStyle","fontWeight","fontFamily","lineHeight","textTransform","letterSpacing"]).length;s code[class*="language-"], -pre[class*="language-"] { - background: #272822; -} - -/* Inline code */ -:not(pre) > code[class*="language-"] { - padding: .1em; - border-radius: .3em; -} - -.token.comment, -.token.prolog, -.token.doctype, -.token.cdata { - color: slategray; -} - -.token.punctuation { - color: #f8f8f2; -} - -.namespace { - opacity: .7; -} - -.token.property, -.token.tag { - color: #f92672; -} - -.token.boolean, -.token.number{ - color: #ae81ff; -} - -.token.selector, -.token.attr-name, -.token.string { - color: #a6e22e; -} - - -.token.operator, -.token.entity, -.token.url, -.language-css .token.string, -.style .token.string { - color: #f8f8f2; -} - -.token.atrule, -.token.attr-value -{ - color: #e6db74; -} - - -.token.keyword{ -color: #66d9ef; -} - -.token.regex, -.token.important { - color: #fd971f; -} - -.token.important { - font-weight: bold; -} - -.token.entity { - cursor: help; -} diff --git a/web/skins/classic/js/chosen/docsupport/prism.js b/web/skins/classic/js/chosen/docsupport/prism.js deleted file mode 100644 index 7ed4fa737..000000000 --- a/web/skins/classic/js/chosen/docsupport/prism.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Prism: Lightweight, robust, elegant syntax highlighting - * MIT license http://www.opensource.org/licenses/mit-license.php/ - * @author Lea Verou http://lea.verou.me - */(function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{type:function(e){return Object.prototype.toString.call(e).match(/\[object (\w+)\]/)[1]},clone:function(e){var n=t.util.type(e);switch(n){case"Object":var r={};for(var i in e)e.hasOwnProperty(i)&&(r[i]=t.util.clone(e[i]));return r;case"Array":return e.slice()}return e}},languages:{extend:function(e,n){var r=t.util.clone(t.languages[e]);for(var i in n)r[i]=n[i];return r},insertBefore:function(e,n,r,i){i=i||t.languages;var s=i[e],o={};for(var u in s)if(s.hasOwnProperty(u)){if(u==n)for(var a in r)r.hasOwnProperty(a)&&(o[a]=r[a]);o[u]=s[u]}return i[e]=o},DFS:function(e,n){for(var r in e){n.call(e,r,e[r]);t.util.type(e)==="Object"&&t.languages.DFS(e[r],n)}}},highlightAll:function(e,n){var r=document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code');for(var i=0,s;s=r[i++];)t.highlightElement(s,e===!0,n)},highlightElement:function(r,i,s){var o,u,a=r;while(a&&!e.test(a.className))a=a.parentNode;if(a){o=(a.className.match(e)||[,""])[1];u=t.languages[o]}if(!u)return;r.className=r.className.replace(e,"").replace(/\s+/g," ")+" language-"+o;a=r.parentNode;/pre/i.test(a.nodeName)&&(a.className=a.className.replace(e,"").replace(/\s+/g," ")+" language-"+o);var f=r.textContent;if(!f)return;f=f.replace(/&/g,"&").replace(/e.length)break e;if(p instanceof i)continue;a.lastIndex=0;var d=a.exec(p);if(d){l&&(c=d[1].length);var v=d.index-1+c,d=d[0].slice(c),m=d.length,g=v+m,y=p.slice(0,v+1),b=p.slice(g+1),w=[h,1];y&&w.push(y);var E=new i(u,f?t.tokenize(d,f):d);w.push(E);b&&w.push(b);Array.prototype.splice.apply(s,w)}}}return s},hooks:{all:{},add:function(e,n){var r=t.hooks.all;r[e]=r[e]||[];r[e].push(n)},run:function(e,n){var r=t.hooks.all[e];if(!r||!r.length)return;for(var i=0,s;s=r[i++];)s(n)}}},n=t.Token=function(e,t){this.type=e;this.content=t};n.stringify=function(e,r,i){if(typeof e=="string")return e;if(Object.prototype.toString.call(e)=="[object Array]")return e.map(function(t){return n.stringify(t,r,e)}).join("");var s={type:e.type,content:n.stringify(e.content,r,i),tag:"span",classes:["token",e.type],attributes:{},language:r,parent:i};s.type=="comment"&&(s.attributes.spellcheck="true");t.hooks.run("wrap",s);var o="";for(var u in s.attributes)o+=u+'="'+(s.attributes[u]||"")+'"';return"<"+s.tag+' class="'+s.classes.join(" ")+'" '+o+">"+s.content+""};if(!self.document){self.addEventListener("message",function(e){var n=JSON.parse(e.data),r=n.language,i=n.code;self.postMessage(JSON.stringify(t.tokenize(i,t.languages[r])));self.close()},!1);return}var r=document.getElementsByTagName("script");r=r[r.length-1];if(r){t.filename=r.src;document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)}})();; -Prism.languages.markup={comment:/<!--[\w\W]*?-->/g,prolog:/<\?.+?\?>/,doctype:/<!DOCTYPE.+?>/,cdata:/<!\[CDATA\[[\w\W]*?]]>/i,tag:{pattern:/<\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|\w+))?\s*)*\/?>/gi,inside:{tag:{pattern:/^<\/?[\w:-]+/i,inside:{punctuation:/^<\/?/,namespace:/^[\w-]+?:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/gi,inside:{punctuation:/=|>|"/g}},punctuation:/\/?>/g,"attr-name":{pattern:/[\w:-]+/g,inside:{namespace:/^[\w-]+?:/}}}},entity:/&#?[\da-z]{1,8};/gi};Prism.hooks.add("wrap",function(e){e.type==="entity"&&(e.attributes.title=e.content.replace(/&/,"&"))});; -Prism.languages.css={comment:/\/\*[\w\W]*?\*\//g,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*{))/gi,inside:{punctuation:/[;:]/g}},url:/url\((["']?).*?\1\)/gi,selector:/[^\{\}\s][^\{\};]*(?=\s*\{)/g,property:/(\b|\B)[\w-]+(?=\s*:)/ig,string:/("|')(\\?.)*?\1/g,important:/\B!important\b/gi,ignore:/&(lt|gt|amp);/gi,punctuation:/[\{\};:]/g};Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{style:{pattern:/(<|<)style[\w\W]*?(>|>)[\w\W]*?(<|<)\/style(>|>)/ig,inside:{tag:{pattern:/(<|<)style[\w\W]*?(>|>)|(<|<)\/style(>|>)/ig,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.css}}});; -Prism.languages.clike={comment:{pattern:/(^|[^\\])(\/\*[\w\W]*?\*\/|(^|[^:])\/\/.*?(\r?\n|$))/g,lookbehind:!0},string:/("|')(\\?.)*?\1/g,"class-name":{pattern:/((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/ig,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|catch|finally|null|break|continue)\b/g,"boolean":/\b(true|false)\b/g,"function":{pattern:/[a-z0-9_]+\(/ig,inside:{punctuation:/\(/}}, number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,operator:/[-+]{1,2}|!|<=?|>=?|={1,3}|(&){1,2}|\|?\||\?|\*|\/|\~|\^|\%/g,ignore:/&(lt|gt|amp);/gi,punctuation:/[{}[\];(),.:]/g};; -Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(var|let|if|else|while|do|for|return|in|instanceof|function|new|with|typeof|try|catch|finally|null|break|continue)\b/g,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?|NaN|-?Infinity)\b/g});Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/g,lookbehind:!0}});Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/(<|<)script[\w\W]*?(>|>)[\w\W]*?(<|<)\/script(>|>)/ig,inside:{tag:{pattern:/(<|<)script[\w\W]*?(>|>)|(<|<)\/script(>|>)/ig,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.javascript}}});; diff --git a/web/skins/classic/js/chosen/docsupport/prism.js.FIXME b/web/skins/classic/js/chosen/docsupport/prism.js.FIXME deleted file mode 100644 index e6283136c..000000000 --- a/web/skins/classic/js/chosen/docsupport/prism.js.FIXME +++ /dev/null @@ -1,3 +0,0 @@ -Outdated, source-less minified file. Original may have been lost. - -https://github.com/harvesthq/chosen/issues/3005 diff --git a/web/skins/classic/js/chosen/docsupport/style.css b/web/skins/classic/js/chosen/docsupport/style.css deleted file mode 100644 index b1d72684d..000000000 --- a/web/skins/classic/js/chosen/docsupport/style.css +++ /dev/null @@ -1,219 +0,0 @@ -/* Reset */ -html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } - -article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } - -blockquote, q { quotes: none; } -blockquote:before, blockquote:after, q:before, q:after { content: ""; content: none; } -ins { background-color: #ff9; color: #000; text-decoration: none; } -mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; } -del { text-decoration: line-through; } -abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; } -table { border-collapse: collapse; border-spacing: 0; } -hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; } -input, select { vertical-align: middle; } - -body { font:13px/1.231 sans-serif; *font-size:small; } /* Hack retained to preserve specificity */ -select, input, textarea, button { font:99% sans-serif; } -pre, code, kbd, samp { font-family: monospace, sans-serif; } - - -body { background: #EEE; color: #444; line-height: 1.4em; } - -header h1 { color: black; font-size: 2em; line-height: 1.1em; display: inline-block; height: 27px; margin: 20px 0 25px; } -header h1 small { font-size: 0.6em; } - -div#content { background: white; border: 1px solid #ccc; border-width: 0 1px 1px; margin: 0 auto; padding: 40px 50px 40px; width: 738px; } - -footer { color: #999; padding-top: 40px; font-size: 0.8em; text-align: center; } - -body { font-family: sans-serif; font-size: 1em; } - -p { margin: 0 0 .7em; max-width: 700px; } -table+p { margin-top: 1em; } - -h2 { border-bottom: 1px solid #ccc; font-size: 1.2em; margin: 3em 0 1em 0; font-weight: bold;} -h3 { font-weight: bold; } - -h2.intro { border-bottom: none; font-size: 1em; font-weight: normal; margin-top:0; } - -ul li { list-style: disc; margin-left: 1em; margin-bottom: 1.25em; } -ol li { margin-left: 1.25em; } -ol ul, ul ul { margin: .25em 0 0; } -ol ul li, ul ul li { list-style-type: circle; margin: 0 0 .25em 1em; } - -li > p { margin-top: .25em; } - -div.side-by-side { width: 100%; margin-bottom: 1em; } -div.side-by-side > div { float: left; width: 49%; } -div.side-by-side > div > em { margin-bottom: 10px; display: block; } - -.faqs em { display: block; } - -.clearfix:after { - content: "\0020"; - display: block; - height: 0; - clear: both; - overflow: hidden; - visibility: hidden; -} - -a { color: #F36C00; outline: none; text-decoration: none; } -a:hover { text-decoration: underline; } - -ul.credits li { margin-bottom: .25em; } - -strong { font-weight: bold; } -i { font-style: italic; } - -.button { - background: #fafafa; - background: -webkit-linear-gradient(top, #ffffff, #eeeeee); - background: -moz-linear-gradient(top, #ffffff, #eeeeee); - background: -o-linear-gradient(top, #ffffff, #eeeeee); - background: linear-gradient(to bottom, #ffffff, #eeeeee); - border: 1px solid #bbbbbb; - border-radius: 4px; - box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.2); - color: #555555; - cursor: pointer; - display: inline-block; - font-family: "Helvetica Neue", Arial, Verdana, "Nimbus Sans L", sans-serif; - font-size: 13px; - font-weight: 500; - height: 31px; - line-height: 28px; - outline: none; - padding: 0 13px; - text-shadow: 0 1px 0 white; - text-decoration: none; - vertical-align: middle; - white-space: nowrap; - -webkit-font-smoothing: antialiased; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} - -.button-blue { - background: #1385e5; - background: -webkit-linear-gradient(top, #53b2fc, #1385e5); - background: -moz-linear-gradient(top, #53b2fc, #1385e5); - background: -o-linear-gradient(top, #53b2fc, #1385e5); - background: linear-gradient(to bottom, #53b2fc, #1385e5); - border-color: #075fa9; - color: white; - font-weight: bold; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.4); -} - - -/* Tweak navbar brand link to be super sleek --------------------------------------------------- */ -.oss-bar { - top: 0; - right: 20px; - position: fixed; - z-index: 1030; -} -.oss-bar ul { - float: right; - margin: 0; - list-style: none; -} -.oss-bar ul li { - list-style: none; - float: left; - line-height: 0; - margin: 0; -} -.oss-bar ul li a { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - -ms-box-sizing: border-box; - box-sizing: border-box; - border: 0; - margin-top: -10px; - display: block; - height: 58px; - background: #F36C00 url(oss-credit.png) no-repeat 20px 22px; - padding: 22px 20px 12px 20px; - text-indent: 120%; /* stupid padding */ - white-space: nowrap; - overflow: hidden; - -webkit-transition: all 0.10s ease-in-out; - -moz-transition: all 0.10s ease-in-out; - transition: all 0.15s ease-in-out; -} -.oss-bar ul li a:hover { - margin-top: 0px; -} -.oss-bar a.harvest { - width: 196px; - background-color: #F36C00; - background-position: -142px 22px; - padding-right: 22px; /* optical illusion */ -} -.oss-bar a.fork { - width: 162px; - background-color: #333333; -} - -.docs-table th, .docs-table td { - border: 1px solid #000; - padding: 4px 6px; - white-space: nowrap; -} - -.docs-table td:last-child { - white-space: normal; -} - -.docs-table th { - font-weight: bold; - text-align: left; -} - -#content pre[class*=language-] { - font-size: 14px; - margin-bottom: 20px; -} - -#content pre[class*=language-] code { - font-size: 14px; - padding: 0; -} - -#content code[class*=language-] { - font-size: 12px; - padding: 2px 4px; -} - -.anchor { - color: inherit; - position: relative; -} - -.anchor:hover { - background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSI3Ij48ZyBmaWxsPSIjNDE0MDQyIj48cGF0aCBkPSJNOS44IDdoLS45bC0uOS0uMWMtLjctLjMtMS40LS43LTEuOC0xLjMtLjItLjEtLjMtLjMtLjMtLjVsLS4zLS40Yy0uMS0uNC0uMi0uOC0uMi0xLjIgMC0uNC4xLS44LjItMS4yaDEuN2MtLjMuNC0uNC44LS40IDEuMiAwIC40LjEuOC4zIDEuMS4xLjIuMi4zLjQuNC4xLjEuMi4yLjQuMy4zLjIuNy4zIDEgLjNoMy40YzEuMiAwIDIuMi0uOSAyLjItMi4xcy0xLTIuMS0yLjItMi4xaC0xLjRjLS4zLS42LS43LTEtMS4yLTEuNGgyLjZjMiAwIDMuNiAxLjYgMy42IDMuNXMtMS42IDMuNS0zLjYgMy41aC0yLjZ6TTguNCAyYy0uMS0uMS0uMi0uMy0uNC0uMy0uMy0uMi0uNy0uMy0xLS4zaC0zLjRjLTEuMiAwLTIuMi45LTIuMiAyLjEgMCAxLjIgMSAyLjEgMi4yIDIuMWgxLjRjLjMuNS43IDEgMS4yIDEuNGgtMi42Yy0yIDAtMy42LTEuNi0zLjYtMy41czEuNi0zLjUgMy42LTMuNWgzLjUwMDAwMDAwMDAwMDAwMDRsLjkuMWMuNy4yIDEuNC43IDEuOCAxLjMuMS4xLjIuMy4zLjUuMS4xLjIuMy4yLjUuMS40LjIuOC4yIDEuMiAwIC40LS4xLjgtLjIgMS4yaC0xLjZjLjMtLjUuNC0uOS40LTEuM3MtLjEtLjgtLjMtMS4xYy0uMS0uMi0uMi0uMy0uNC0uNHoiLz48L2c+PC9zdmc+) 0 50% no-repeat; - background-size: 21px 9px; - margin-left: -27px; - padding-left: 27px; - text-decoration: none; -} - -.select, -.chosen-select, -.chosen-select-no-single, -.chosen-select-no-results, -.chosen-select-deselect, -.chosen-select-rtl, -.chosen-select-width { - width: 350px; -} - -.jquery-version-refer { - margin-top: 40px; - font-style: italic; -} diff --git a/web/skins/classic/js/chosen/index.html b/web/skins/classic/js/chosen/index.html deleted file mode 100644 index 913bfd2a3..000000000 --- a/web/skins/classic/js/chosen/index.html +++ /dev/null @@ -1,1473 +0,0 @@ - - - - - Chosen: A jQuery Plugin by Harvest to Tame Unwieldy Select Boxes - - - - - - - - -
                  -
                  -
                  -
                  -

                  Chosen (v1.8.2)

                  -
                  -

                  Chosen is a jQuery plugin that makes long, unwieldy select boxes much more user-friendly.

                  - -

                  - Downloads - Project Source - Contribute -

                  - -

                  Standard Select

                  -
                  -
                  - Turns This - -
                  -
                  - Into This - -
                  -
                  - -

                  Multiple Select

                  -
                  -
                  - Turns This - -
                  -
                  - Into This - -
                  -
                  - -

                  <optgroup> Support

                  -
                  -
                  - Single Select with Groups - -
                  -
                  - Multiple Select with Groups - -
                  -
                  - -

                  Selected and Disabled Support

                  -
                  -

                  Chosen automatically highlights selected options and removes disabled options.

                  -
                  - Single Select - -
                  -
                  - Multiple Select - -
                  -
                  - -

                  Hide Search on Single Select

                  -
                  -

                  The disable_search_threshold option can be specified to hide the search input on single selects if there are n or fewer options.

                  -
                  $(".chosen-select").chosen({disable_search_threshold: 10});
                  -

                  -
                  - -
                  -
                  - -

                  Default Text Support

                  -
                  -

                  Chosen automatically sets the default field text ("Choose a country...") by reading the select element's data-placeholder value. If no data-placeholder value is present, it will default to "Select an Option" or "Select Some Options" depending on whether the select is single or multiple. You can change these elements in the plugin js file as you see fit.

                  -
                  <select data-placeholder="Choose a country..." multiple class="chosen-select">
                  -

                  Note: on single selects, the first element is assumed to be selected by the browser. To take advantage of the default text support, you will need to include a blank option as the first element of your select list.

                  -
                  - -

                  No Results Text Support

                  -
                  -

                  Setting the "No results" search text is as easy as passing an option when you create Chosen:

                  -
                   $(".chosen-select").chosen({no_results_text: "Oops, nothing found!"}); 
                  -

                  -
                  - Single Select - -
                  -
                  - Multiple Select - -
                  -
                  - -

                  Limit Selected Options in Multiselect

                  -
                  -

                  You can easily limit how many options the user can select:

                  -
                  $(".chosen-select").chosen({max_selected_options: 5});
                  -

                  If you try to select another option with limit reached chosen:maxselected event is triggered:

                  -
                   $(".chosen-select").bind("chosen:maxselected", function () { ... }); 
                  -
                  - -

                  Allow Deselect on Single Selects

                  -
                  -

                  When a single select box isn't a required field, you can set allow_single_deselect: true and Chosen will add a UI element for option deselection. This will only work if the first option has blank text.

                  -
                  - -
                  -
                  - -

                  Right-to-Left Support

                  -
                  -

                  You can set right-to-left text by setting rtl: true

                  -
                   $(".chosen-select").chosen({rtl: true}); 
                  - -
                  - Single Right-to-Left Select - -
                  -
                  - Multiple Right-to-Left Select - -
                  -
                  - -

                  Observing, Updating, and Destroying Chosen

                  -
                  -
                    -
                  • -

                    Observing Form Field Changes

                    -

                    When working with form fields, you often want to perform some behavior after a value has been selected or deselected. Whenever a user selects a field in Chosen, it triggers a "change" event on the original form field. That lets you do something like this:

                    -
                    $("#form_field").chosen().change( … );
                    -
                  • -
                  • -

                    Updating Chosen Dynamically

                    -

                    If you need to update the options in your select field and want Chosen to pick up the changes, you'll need to trigger the "chosen:updated" event on the field. Chosen will re-build itself based on the updated content.

                    -
                    $("#form_field").trigger("chosen:updated");
                    -
                  • -
                  • -

                    Destroying Chosen

                    -

                    To destroy Chosen and revert back to the native select:

                    -
                    $("#form_field").chosen("destroy");
                    -
                  • -
                  -
                  - -

                  Custom Width Support

                  -
                  -

                  Using a custom width with Chosen is as easy as passing an option when you create Chosen:

                  -
                   $(".chosen-select").chosen({width: "95%"}); 
                  -
                  - Single Select - -
                  -
                  - Multiple Select - -
                  -
                  - -

                  Labels work, too

                  -
                  -

                  Use labels just like you would a standard select

                  -

                  -
                  - - -
                  -
                  - - -
                  -
                  - -

                  Setup

                  -

                  Using Chosen is easy as can be.

                  -
                    -
                  1. Download the plugin and copy the chosen files to your app.
                  2. -
                  3. Activate the plugin on the select boxes of your choice: $(".chosen-select").chosen()
                  4. -
                  5. Disco.
                  6. -
                  - -

                  FAQs

                  -
                    -
                  • -

                    Do you have all the available options documented somewhere?

                    -

                    Yes! You can find them on the options page.

                    -
                  • -
                  • -

                    Something doesn't work. Can you fix it?

                    -

                    Yes! Please report all issues using the GitHub issue tracking tool. Please include the plugin version (jQuery or Prototype), browser and OS. The more information provided, the easier it is to fix a problem.

                    -
                  • -
                  • -

                    What browsers are supported?

                    -

                    All modern desktop browsers are supported (Firefox, Chrome, Safari and IE9). Legacy support for IE8 is also enabled. Chosen is disabled on iPhone, iPod Touch, and Android mobile devices (more information).

                    -
                  • -
                  • -

                    Didn't there used to be a Prototype version of Chosen?

                    -

                    There still is!

                    -
                  • -
                  - -

                  Credits

                  - - - - - -
                  -
                  - - - - -
                  - - - diff --git a/web/skins/classic/js/chosen/index.proto.html b/web/skins/classic/js/chosen/index.proto.html deleted file mode 100644 index 11c9346d6..000000000 --- a/web/skins/classic/js/chosen/index.proto.html +++ /dev/null @@ -1,1472 +0,0 @@ - - - - - Chosen: A Prototype Plugin by Harvest to Tame Unwieldy Select Boxes - - - - - - - - -
                  -
                  -
                  -

                  Chosen - Prototype Version (v1.8.2)

                  -
                  -

                  Chosen is a Prototype plugin that makes long, unwieldy select boxes much more user-friendly.

                  - -

                  - Downloads - Project Source - Contribute -

                  - -

                  Looking for the jQuery version?

                  - -

                  Standard Select

                  -
                  -
                  - Turns This - -
                  -
                  - Into This - -
                  -
                  - -

                  Multiple Select

                  -
                  -
                  - Turns This - -
                  -
                  - Into This - -
                  -
                  - -

                  <optgroup> Support

                  -
                  -
                  - Single Select with Groups - -
                  -
                  - Multiple Select with Groups - -
                  -
                  - -

                  Selected and Disabled Support

                  -
                  -

                  Chosen automatically highlights selected options and removes disabled options.

                  -
                  - Single Select - -
                  -
                  - Multiple Select - -
                  -
                  - -

                  Hide Search on Single Select

                  -
                  -

                  The disable_search_threshold option can be specified to hide the search input on single selects if there are n or fewer options.

                  -
                   new Chosen($("chosen_select_field"),{disable_search_threshold: 10}); 
                  -

                  -
                  - -
                  -
                  - -

                  Default Text Support

                  -
                  -

                  Chosen automatically sets the default field text ("Choose a country...") by reading the select element's data-placeholder value. If no data-placeholder value is present, it will default to "Select an Option" or "Select Some Options" depending on whether the select is single or multiple. You can change these elements in the plugin js file as you see fit.

                  -
                  <select data-placeholder="Choose a country..." multiple class="chosen-select">
                  -

                  Note: on single selects, the first element is assumed to be selected by the browser. To take advantage of the default text support, you will need to include a blank option as the first element of your select list.

                  -
                  - -

                  No Results Text Support

                  -
                  -

                  Setting the "No results" search text is as easy as passing an option when you create Chosen:

                  -
                  new Chosen($("chosen_select_field"),{no_results_text: "Oops, nothing found!"}); 
                  - -
                  - Single Select - -
                  -
                  - Multiple Select - -
                  -
                  - -

                  Limit Selected Options in Multiselect

                  -
                  -

                  You can easily limit how many options the user can select:

                  -
                  new Chosen($("chosen_select_field"),{max_selected_options: 5}); 
                  -

                  If you try to select another option with limit reached chosen:maxselected event is triggered:

                  -
                  $("chosen_select_field").observe("chosen:maxselected", function(evt) { ... }); 
                  -
                  - -

                  Allow Deselect on Single Selects

                  -
                  -

                  When a single select box isn't a required field, you can set allow_single_deselect: true and Chosen will add a UI element for option deselection. This will only work if the first option has blank text.

                  -
                  - -
                  -
                  - -

                  Right-to-Left Support

                  -
                  -

                  You can set right-to-left text by setting rtl: true

                  -
                   $(".chosen-select").chosen({rtl: true}); 
                  -
                  - Single Right-to-Left Select - -
                  -
                  - Multiple Right-to-Left Select - -
                  -
                  - -

                  Observing, Updating, and Destroying Chosen

                  -
                  -
                    -
                  • -

                    Observing Form Field Changes

                    -

                    When working with form fields, you often want to perform some behavior after a value has been selected or deselected. Whenever a user selects a field in Chosen, it triggers a "change" event on the original form field. That lets you do something like this:

                    -
                    $("#form_field").chosen().change( … );
                    -

                    Note: Prototype doesn't offer support for triggering standard browser events. Event.simulate is required to trigger the change event when using the Prototype version.

                    -
                  • -
                  • -

                    Updating Chosen Dynamically

                    -

                    If you need to update the options in your select field and want Chosen to pick up the changes, you'll need to trigger the "chosen:updated" event on the field. Chosen will re-build itself based on the updated content.

                    -
                    Event.fire($("form_field"), "chosen:updated");
                    -
                  • -
                  • -

                    Destroying Chosen

                    -

                    To destroy Chosen and revert back to the native select, call destroy on the Chosen instance:

                    -
                    chosen = new Chosen($("form_field"));
                    -
                    -// ...later
                    -chosen.destroy();
                    -
                  • -
                  -
                  - -

                  Custom Width Support

                  -
                  -

                  Using a custom width with Chosen is as easy as passing an option when you create Chosen:

                  -
                  new Chosen($("chosen_select_field"),{width: "95%"}); 
                  -
                  - Single Select - -
                  -
                  - Multiple Select - -
                  -
                  - -

                  Labels work, too

                  -
                  -

                  Use labels just like you would a standard select

                  -

                  -
                  - - -
                  -
                  - - -
                  -
                  - -

                  Setup

                  -

                  Using Chosen is easy as can be.

                  -
                    -
                  1. Download the plugin and copy the chosen files to your app.
                  2. -
                  3. Activate the plugin by creating a new instance of Chosen: New Chosen(some_form_field,some_options);
                  4. -
                  5. Disco.
                  6. -
                  - -

                  FAQs

                  -
                    -
                  • -

                    Do you have all the available options documented somewhere?

                    -

                    Yes! You can find them on the options page.

                    -
                  • -
                  • -

                    Something doesn't work. Can you fix it?

                    -

                    Yes! Please report all issues using the GitHub issue tracking tool. Please include the plugin version (jQuery or Prototype), browser and OS. The more information provided, the easier it is to fix a problem.

                    -
                  • -
                  • -

                    What browsers are supported?

                    -

                    All modern desktop browsers are supported (Firefox, Chrome, Safari and IE9). Legacy support for IE8 is also enabled. Chosen is disabled on iPhone, iPod Touch, and Android mobile devices (more information).

                    -
                  • -
                  - -

                  Credits

                  - - - - - -
                  -
                  - - - - - - - diff --git a/web/skins/classic/js/chosen/options.html b/web/skins/classic/js/chosen/options.html deleted file mode 100644 index 3e2a6a87e..000000000 --- a/web/skins/classic/js/chosen/options.html +++ /dev/null @@ -1,306 +0,0 @@ - - - - - Chosen: A jQuery Plugin by Harvest to Tame Unwieldy Select Boxes - - - - - - -
                  -
                  -
                  -

                  Chosen (v1.8.2)

                  -
                  -

                  Chosen has a number of options and attributes that allow you to have full control of your select boxes.

                  - -

                  Options

                  -

                  The following options are available to pass into Chosen on instantiation.

                  - -

                  Example:

                  -
                  -  $(".my_select_box").chosen({
                  -    disable_search_threshold: 10,
                  -    no_results_text: "Oops, nothing found!",
                  -    width: "95%"
                  -  });
                  -
                  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
                  OptionDefaultDescription
                  allow_single_deselectfalseWhen set to true on a single select, Chosen adds a UI element which selects the first element (if it is blank).
                  disable_searchfalseWhen set to true, Chosen will not display the search field (single selects only).
                  disable_search_threshold0Hide the search input on single selects if there are n or fewer options.
                  enable_split_word_searchtrueBy default, searching will match on any word within an option tag. Set this option to false if you want to only match on the entire text of an option tag.
                  inherit_select_classesfalseWhen set to true, Chosen will grab any classes on the original select field and add them to Chosen’s container div.
                  max_selected_optionsInfinityLimits how many options the user can select. When the limit is reached, the chosen:maxselected event is triggered.
                  no_results_text"No results match"The text to be displayed when no matching results are found. The current search is shown at the end of the text (e.g., - No results match "Bad Search").
                  placeholder_text_multiple"Select Some Options"The text to be displayed as a placeholder when no options are selected for a multiple select.
                  placeholder_text_single"Select an Option"The text to be displayed as a placeholder when no options are selected for a single select.
                  search_containsfalseBy default, Chosen’s search matches starting at the beginning of a word. Setting this option to true allows matches starting from anywhere within a word. This is especially useful for options that include a lot of special characters or phrases in ()s and []s.
                  single_backstroke_deletetrueBy default, pressing delete/backspace on multiple selects will remove a selected choice. When false, pressing delete/backspace will highlight the last choice, and a second press deselects it.
                  widthOriginal select width.The width of the Chosen select box. By default, Chosen attempts to match the width of the select box you are replacing. If your select is hidden when Chosen is instantiated, you must specify a width or the select will show up with a width of 0.
                  display_disabled_optionstrueBy default, Chosen includes disabled options in search results with a special styling. Setting this option to false will hide disabled results and exclude them from searches.
                  display_selected_optionstrue -

                  By default, Chosen includes selected options in search results with a special styling. Setting this option to false will hide selected results and exclude them from searches.

                  -

                  Note: this is for multiple selects only. In single selects, the selected result will always be displayed.

                  -
                  include_group_label_in_selectedfalse -

                  By default, Chosen only shows the text of a selected option. Setting this option to true will show the text and group (if any) of the selected option.

                  -
                  max_shown_resultsInfinity -

                  Only show the first (n) matching options in the results. This can be used to increase performance for selects with very many options.

                  -
                  case_sensitive_searchfalse -

                  By default Chosen's search is case-insensitive. Setting this option to true makes the search case-sensitive.

                  -
                  hide_results_on_selecttrue -

                  By default Chosen's results are hidden after a option is selected. Setting this option to false will keep the results open after selection. This only applies to multiple selects.

                  -
                  rtlfalse -

                  Chosen supports right-to-left text in select boxes. Set this option to true to support right-to-left text options.

                  -

                  Note: the chosen-rtl class on the select has precedence over this option. However, the classname approach is deprecated and will be removed in future versions of Chosen.

                  -
                  - -

                  Attributes

                  -

                  Certain attributes placed on the select tag or its options can be used to configure Chosen.

                  - -

                  Example:

                  - -
                  -  <select class="my_select_box" data-placeholder="Select Your Options">
                  -    <option value="1">Option 1</option>
                  -    <option value="2" selected>Option 2</option>
                  -    <option value="3" disabled>Option 3</option>
                  -  </select>
                  -
                  - - - - - - - - - - - - - - - - - -
                  AttributeDescription
                  data-placeholder -

                  The text to be displayed as a placeholder when no options are selected for a select. Defaults to "Select an Option" for single selects or "Select Some Options" for multiple selects.

                  -

                  Note:This attribute overrides anything set in the placeholder_text_multiple or placeholder_text_single options.

                  -
                  multipleThe attribute multiple on your select box dictates whether Chosen will render a multiple or single select.
                  selected, disabledChosen automatically highlights selected options and disables disabled options.
                  - -

                  Classes

                  -

                  Classes placed on the select tag can be used to configure Chosen.

                  - -

                  Example:

                  - -
                  -  <select class="my_select_box chosen-rtl">
                  -    <option value="1">Option 1</option>
                  -    <option value="2">Option 2</option>
                  -    <option value="3">Option 3</option>
                  -  </select>
                  -
                  - - - - - - - - - - -
                  ClassnameDescription
                  chosen-rtl -

                  Chosen supports right-to-left text in select boxes. Add the class chosen-rtl to your select tag to support right-to-left text options.

                  -

                  Note: The chosen-rtl class will pass through to the Chosen select even when the inherit_select_classes option is set to false.

                  -

                  Note: This is deprecated in favor of using the rtl: true option (see the Options section).

                  -
                  - -

                  Triggered Events

                  -

                  Chosen triggers a number of standard and custom events on the original select field.

                  - -

                  Example:

                  - -
                  -  $('.my_select_box').on('change', function(evt, params) {
                  -    do_something(evt, params);
                  -  });
                  -
                  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
                  EventDescription
                  change -

                  Chosen triggers the standard DOM event whenever a selection is made (it also sends a selected or deselected parameter that tells you which option was changed).

                  -

                  Note: The selected and deselected parameters are not available for Prototype.

                  -
                  chosen:readyTriggered after Chosen has been fully instantiated.
                  chosen:maxselectedTriggered if max_selected_options is set and that total is broken.
                  chosen:showing_dropdownTriggered when Chosen’s dropdown is opened.
                  chosen:hiding_dropdownTriggered when Chosen’s dropdown is closed.
                  chosen:no_resultsTriggered when a search returns no matching results.
                  - -

                  - Note: all custom Chosen events (those that begin with chosen:) also include the chosen object as a parameter. -

                  - -

                  Triggerable Events

                  -

                  You can trigger several events on the original select field to invoke a behavior in Chosen.

                  - -

                  Example:

                  - -
                  -  // tell Chosen that a select has changed
                  -  $('.my_select_box').trigger('chosen:updated');
                  -
                  - - - - - - - - - - - - - - - - - - - - - -
                  EventDescription
                  chosen:updatedThis event should be triggered whenever Chosen’s underlying select element changes (such as a change in selected options).
                  chosen:activateThis is the equivalant of focusing a standard HTML select field. When activated, Chosen will capure keypress events as if you had clicked the field directly.
                  chosen:openThis event activates Chosen and also displays the search results.
                  chosen:closeThis event deactivates Chosen and hides the search results.
                  - - - -
                  -
                  - - - - diff --git a/web/skins/classic/js/chosen/package.json b/web/skins/classic/js/chosen/package.json deleted file mode 100644 index 20c4480a3..000000000 --- a/web/skins/classic/js/chosen/package.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "name": "chosen-js", - "version": "1.8.1", - "description": "Chosen is a JavaScript plugin that makes select boxes user-friendly. It is currently available in both jQuery and Prototype flavors.", - "keywords": [ - "select", - "multiselect", - "dropdown", - "form", - "input", - "ui" - ], - "homepage": "https://harvesthq.github.io/chosen/", - "bugs": "https://github.com/harvesthq/chosen/issues", - "license": "MIT", - "contributors": [ - { - "name": "Patrick Filler", - "url": "https://github.com/pfiller" - }, - { - "name": "Christophe Coevoet", - "url": "https://github.com/stof" - }, - { - "name": "Ken Earley", - "url": "https://github.com/kenearley" - }, - { - "name": "Koen Punt", - "url": "https://github.com/koenpunt" - } - ], - "dependencies": {}, - "files": [ - "chosen.jquery.js", - "chosen.jquery.min.js", - "chosen.proto.js", - "chosen.proto.min.js", - "chosen.css", - "chosen.min.css", - "chosen-sprite@2x.png", - "chosen-sprite.png" - ], - "main": "chosen.jquery.js", - "repository": { - "type": "git", - "url": "https://github.com/harvesthq/chosen.git" - } -} diff --git a/web/skins/classic/js/classic.js b/web/skins/classic/js/classic.js index 041d52dea..b3b72d967 100644 --- a/web/skins/classic/js/classic.js +++ b/web/skins/classic/js/classic.js @@ -23,52 +23,54 @@ // // Javascript window sizes +/* eslint-disable key-spacing */ var popupSizes = { - 'bandwidth': { 'width': 300, 'height': 220 }, - 'console': { 'width': 750, 'height': 312 }, - 'control': { 'width': 380, 'height': 480 }, - 'controlcaps': { 'width': 780, 'height': 320 }, - 'controlcap': { 'width': 400, 'height': 400 }, - 'cycle': { 'addWidth': 32, 'minWidth': 384, 'addHeight': 62 }, - 'device': { 'width': 260, 'height': 150 }, - 'devices': { 'width': 400, 'height': 240 }, - 'donate': { 'width': 500, 'height': 280 }, - 'download': { 'width': 350, 'height': 215 }, - 'event': { 'addWidth': 108, 'minWidth': 496, 'addHeight': 230, 'minHeight': 540 }, - 'eventdetail': { 'width': 600, 'height': 420 }, - 'events': { 'width': 1020, 'height': 780 }, - 'export': { 'width': 400, 'height': 340 }, - 'filter': { 'width': 820, 'height': 700 }, - 'frame': { 'addWidth': 32, 'minWidth': 384, 'addHeight': 200 }, - 'frames': { 'width': 600, 'height': 700 }, - 'function': { 'width': 400, 'height': 250 }, - 'group': { 'width': 660, 'height': 520 }, - 'groups': { 'width': 440, 'height': 220 }, - 'image': { 'addWidth': 48, 'addHeight': 80 }, - 'log': { 'width': 1080, 'height': 720 }, - 'login': { 'width': 720, 'height': 480 }, - 'logout': { 'width': 460, 'height': 300 }, - 'monitor': { 'width': 700, 'height': 680 }, - 'monitorpreset':{ 'width': 440, 'height': 200 }, - 'monitorprobe': { 'width': 500, 'height': 240 }, - 'monitorselect':{ 'width': 160, 'height': 200 }, - 'monitors': { 'width': 300, 'height': 640 }, - 'montage': { 'width': -1, 'height': -1 }, - 'onvifprobe': { 'width': 700, 'height': 550 }, - 'optionhelp': { 'width': 400, 'height': 320 }, - 'options': { 'width': 1000, 'height': 660 }, - 'preset': { 'width': 300, 'height': 120 }, - 'server': { 'width': 600, 'height': 405 }, - 'settings': { 'width': 220, 'height': 225 }, - 'state': { 'width': 370, 'height': 134 }, - 'stats': { 'width': 840, 'height': 200 }, - 'storage': { 'width': 600, 'height': 405 }, - 'timeline': { 'width': 760, 'height': 540 }, - 'user': { 'width': 360, 'height': 720 }, - 'version': { 'width': 360, 'height': 140 }, - 'video': { 'width': 420, 'height': 360 }, - 'videoview': { 'addWidth': 48, 'addHeight': 80 }, - 'watch': { 'addWidth': 96, 'minWidth': 420, 'addHeight': 384 }, - 'zone': { 'addWidth': 450, 'addHeight': 200, 'minHeight': 450 }, - 'zones': { 'addWidth': 72, 'addHeight': 232 } + 'bandwidth': {'width': 300, 'height': 220}, + 'console': {'width': 750, 'height': 312}, + 'control': {'width': 380, 'height': 480}, + 'controlcaps': {'width': 780, 'height': 320}, + 'controlcap': {'width': 600, 'height': 400}, + 'cycle': {'addWidth': 32, 'minWidth': 384, 'addHeight': 62}, + 'device': {'width': 260, 'height': 150}, + 'devices': {'width': 400, 'height': 240}, + 'donate': {'width': 500, 'height': 480}, + 'download': {'width': 350, 'height': 215}, + 'event': {'addWidth': 108, 'minWidth': 496, 'addHeight': 230, 'minHeight': 540}, + 'eventdetail': {'width': 600, 'height': 420}, + 'events': {'width': 1020, 'height': 780}, + 'export': {'width': 400, 'height': 340}, + 'filter': {'width': 820, 'height': 700}, + 'frame': {'addWidth': 32, 'minWidth': 384, 'addHeight': 200}, + 'frames': {'width': 600, 'height': 700}, + 'function': {'width': 400, 'height': 250}, + 'group': {'width': 660, 'height': 520}, + 'groups': {'width': 440, 'height': 220}, + 'image': {'addWidth': 48, 'addHeight': 80}, + 'log': {'width': 1080, 'height': 720}, + 'login': {'width': 720, 'height': 480}, + 'logout': {'width': 460, 'height': 300}, + 'monitor': {'width': 700, 'height': 680}, + 'monitorpreset':{'width': 440, 'height': 200}, + 'monitorprobe': {'width': 500, 'height': 240}, + 'monitorselect':{'width': 160, 'height': 200}, + 'monitors': {'width': 300, 'height': 640}, + 'montage': {'width': -1, 'height': -1}, + 'onvifprobe': {'width': 700, 'height': 550}, + 'optionhelp': {'width': 400, 'height': 320}, + 'options': {'width': 1000, 'height': 660}, + 'preset': {'width': 300, 'height': 120}, + 'server': {'width': 600, 'height': 405}, + 'settings': {'width': 220, 'height': 225}, + 'state': {'width': 370, 'height': 134}, + 'stats': {'width': 840, 'height': 200}, + 'storage': {'width': 600, 'height': 405}, + 'timeline': {'width': 760, 'height': 540}, + 'user': {'width': 360, 'height': 720}, + 'version': {'width': 360, 'height': 140}, + 'video': {'width': 420, 'height': 360}, + 'videoview': {'addWidth': 48, 'addHeight': 80}, + 'watch': {'addWidth': 96, 'minWidth': 420, 'addHeight': 384}, + 'zone': {'addWidth': 450, 'addHeight': 200, 'minHeight': 450}, + 'zones': {'addWidth': 72, 'addHeight': 232} }; +/* eslint-enable key-spacing */ diff --git a/web/skins/classic/js/vjs.eot b/web/skins/classic/js/font/vjs.eot similarity index 100% rename from web/skins/classic/js/vjs.eot rename to web/skins/classic/js/font/vjs.eot diff --git a/web/skins/classic/js/vjs.ttf b/web/skins/classic/js/font/vjs.ttf similarity index 100% rename from web/skins/classic/js/vjs.ttf rename to web/skins/classic/js/font/vjs.ttf diff --git a/web/skins/classic/js/vjs.woff b/web/skins/classic/js/font/vjs.woff similarity index 100% rename from web/skins/classic/js/vjs.woff rename to web/skins/classic/js/font/vjs.woff diff --git a/web/skins/classic/js/jquery-1.11.3.js b/web/skins/classic/js/jquery-2.2.4.js similarity index 60% rename from web/skins/classic/js/jquery-1.11.3.js rename to web/skins/classic/js/jquery-2.2.4.js index 6feb11086..5c3c456ac 100644 --- a/web/skins/classic/js/jquery-1.11.3.js +++ b/web/skins/classic/js/jquery-2.2.4.js @@ -1,27 +1,27 @@ /*! - * jQuery JavaScript Library v1.11.3 + * jQuery JavaScript Library v2.2.4 * http://jquery.com/ * * Includes Sizzle.js * http://sizzlejs.com/ * - * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors + * Copyright jQuery Foundation and other contributors * Released under the MIT license * http://jquery.org/license * - * Date: 2015-04-28T16:19Z + * Date: 2016-05-20T17:23Z */ (function( global, factory ) { if ( typeof module === "object" && typeof module.exports === "object" ) { - // For CommonJS and CommonJS-like environments where a proper window is present, - // execute the factory and get jQuery - // For environments that do not inherently posses a window with a document - // (such as Node.js), expose a jQuery-making factory as module.exports - // This accentuates the need for the creation of a real window + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. // e.g. var jQuery = require("jquery")(window); - // See ticket #14549 for more info + // See ticket #14549 for more info. module.exports = global.document ? factory( global, true ) : function( w ) { @@ -37,21 +37,22 @@ // Pass this if window is not defined yet }(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { -// Can't do this because several apps including ASP.NET trace +// Support: Firefox 18+ +// Can't be in strict mode, several libs including ASP.NET trace // the stack via arguments.caller.callee and Firefox dies if // you try to trace through "use strict" call chains. (#13335) -// Support: Firefox 18+ -// +//"use strict"; +var arr = []; -var deletedIds = []; +var document = window.document; -var slice = deletedIds.slice; +var slice = arr.slice; -var concat = deletedIds.concat; +var concat = arr.concat; -var push = deletedIds.push; +var push = arr.push; -var indexOf = deletedIds.indexOf; +var indexOf = arr.indexOf; var class2type = {}; @@ -64,16 +65,17 @@ var support = {}; var - version = "1.11.3", + version = "2.2.4", // Define a local copy of jQuery jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' // Need init if jQuery is called (just allow error to be thrown if not included) return new jQuery.fn.init( selector, context ); }, - // Support: Android<4.1, IE<9 + // Support: Android<4.1 // Make sure we trim BOM and NBSP rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, @@ -87,6 +89,7 @@ var }; jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used jquery: version, @@ -130,16 +133,14 @@ jQuery.fn = jQuery.prototype = { }, // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); + each: function( callback ) { + return jQuery.each( this, callback ); }, map: function( callback ) { - return this.pushStack( jQuery.map(this, function( elem, i ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { return callback.call( elem, i, elem ); - })); + } ) ); }, slice: function() { @@ -157,23 +158,23 @@ jQuery.fn = jQuery.prototype = { eq: function( i ) { var len = this.length, j = +i + ( i < 0 ? len : 0 ); - return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); }, end: function() { - return this.prevObject || this.constructor(null); + return this.prevObject || this.constructor(); }, // For internal use only. // Behaves like an Array's method, not like a jQuery method. push: push, - sort: deletedIds.sort, - splice: deletedIds.splice + sort: arr.sort, + splice: arr.splice }; jQuery.extend = jQuery.fn.extend = function() { - var src, copyIsArray, copy, name, options, clone, - target = arguments[0] || {}, + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, i = 1, length = arguments.length, deep = false; @@ -182,25 +183,27 @@ jQuery.extend = jQuery.fn.extend = function() { if ( typeof target === "boolean" ) { deep = target; - // skip the boolean and the target + // Skip the boolean and the target target = arguments[ i ] || {}; i++; } // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { target = {}; } - // extend jQuery itself if only one argument is passed + // Extend jQuery itself if only one argument is passed if ( i === length ) { target = this; i--; } for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { + if ( ( options = arguments[ i ] ) != null ) { + // Extend the base object for ( name in options ) { src = target[ name ]; @@ -212,13 +215,15 @@ jQuery.extend = jQuery.fn.extend = function() { } // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = jQuery.isArray( copy ) ) ) ) { + if ( copyIsArray ) { copyIsArray = false; - clone = src && jQuery.isArray(src) ? src : []; + clone = src && jQuery.isArray( src ) ? src : []; } else { - clone = src && jQuery.isPlainObject(src) ? src : {}; + clone = src && jQuery.isPlainObject( src ) ? src : {}; } // Never move original objects, clone them @@ -236,7 +241,8 @@ jQuery.extend = jQuery.fn.extend = function() { return target; }; -jQuery.extend({ +jQuery.extend( { + // Unique for each copy of jQuery on the page expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), @@ -249,28 +255,49 @@ jQuery.extend({ noop: function() {}, - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). isFunction: function( obj ) { - return jQuery.type(obj) === "function"; + return jQuery.type( obj ) === "function"; }, - isArray: Array.isArray || function( obj ) { - return jQuery.type(obj) === "array"; - }, + isArray: Array.isArray, isWindow: function( obj ) { - /* jshint eqeqeq: false */ - return obj != null && obj == obj.window; + return obj != null && obj === obj.window; }, isNumeric: function( obj ) { + // parseFloat NaNs numeric-cast false positives (null|true|false|"") // ...but misinterprets leading-number strings, particularly hex literals ("0x...") // subtraction forces infinities to NaN // adding 1 corrects loss of precision from parseFloat (#15100) - return !jQuery.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0; + var realStringObj = obj && obj.toString(); + return !jQuery.isArray( obj ) && ( realStringObj - parseFloat( realStringObj ) + 1 ) >= 0; + }, + + isPlainObject: function( obj ) { + var key; + + // Not plain objects: + // - Any object or value whose internal [[Class]] property is not "[object Object]" + // - DOM nodes + // - window + if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call( obj, "constructor" ) && + !hasOwn.call( obj.constructor.prototype || {}, "isPrototypeOf" ) ) { + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); }, isEmptyObject: function( obj ) { @@ -281,67 +308,45 @@ jQuery.extend({ return true; }, - isPlainObject: function( obj ) { - var key; - - // Must be an Object. - // Because of IE, we also have to check the presence of the constructor property. - // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { - return false; - } - - try { - // Not own constructor property must be Object - if ( obj.constructor && - !hasOwn.call(obj, "constructor") && - !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { - return false; - } - } catch ( e ) { - // IE8,9 Will throw exceptions on certain host objects #9897 - return false; - } - - // Support: IE<9 - // Handle iteration over inherited properties before own properties. - if ( support.ownLast ) { - for ( key in obj ) { - return hasOwn.call( obj, key ); - } - } - - // Own properties are enumerated firstly, so to speed up, - // if last one is own, then all properties are own. - for ( key in obj ) {} - - return key === undefined || hasOwn.call( obj, key ); - }, - type: function( obj ) { if ( obj == null ) { return obj + ""; } + + // Support: Android<4.0, iOS<6 (functionish RegExp) return typeof obj === "object" || typeof obj === "function" ? - class2type[ toString.call(obj) ] || "object" : + class2type[ toString.call( obj ) ] || "object" : typeof obj; }, // Evaluates a script in a global context - // Workarounds based on findings by Jim Driscoll - // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context - globalEval: function( data ) { - if ( data && jQuery.trim( data ) ) { - // We use execScript on Internet Explorer - // We use an anonymous function so that context is window - // rather than jQuery in Firefox - ( window.execScript || function( data ) { - window[ "eval" ].call( window, data ); - } )( data ); + globalEval: function( code ) { + var script, + indirect = eval; + + code = jQuery.trim( code ); + + if ( code ) { + + // If the code includes a valid, prologue position + // strict mode pragma, execute code by injecting a + // script tag into the document. + if ( code.indexOf( "use strict" ) === 1 ) { + script = document.createElement( "script" ); + script.text = code; + document.head.appendChild( script ).parentNode.removeChild( script ); + } else { + + // Otherwise, avoid the DOM node creation, insertion + // and removal by using an indirect global eval + + indirect( code ); + } } }, // Convert dashed to camelCase; used by the css and data modules + // Support: IE9-11+ // Microsoft forgot to hump their vendor prefix (#9572) camelCase: function( string ) { return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); @@ -351,49 +356,20 @@ jQuery.extend({ return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); }, - // args is for internal usage only - each: function( obj, callback, args ) { - var value, - i = 0, - length = obj.length, - isArray = isArraylike( obj ); + each: function( obj, callback ) { + var length, i = 0; - if ( args ) { - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback.apply( obj[ i ], args ); - - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.apply( obj[ i ], args ); - - if ( value === false ) { - break; - } + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; } } - - // A special, fast, case for the most common use of each } else { - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback.call( obj[ i ], i, obj[ i ] ); - - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.call( obj[ i ], i, obj[ i ] ); - - if ( value === false ) { - break; - } + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; } } } @@ -401,7 +377,7 @@ jQuery.extend({ return obj; }, - // Support: Android<4.1, IE<9 + // Support: Android<4.1 trim: function( text ) { return text == null ? "" : @@ -413,7 +389,7 @@ jQuery.extend({ var ret = results || []; if ( arr != null ) { - if ( isArraylike( Object(arr) ) ) { + if ( isArrayLike( Object( arr ) ) ) { jQuery.merge( ret, typeof arr === "string" ? [ arr ] : arr @@ -427,25 +403,7 @@ jQuery.extend({ }, inArray: function( elem, arr, i ) { - var len; - - if ( arr ) { - if ( indexOf ) { - return indexOf.call( arr, elem, i ); - } - - len = arr.length; - i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; - - for ( ; i < len; i++ ) { - // Skip accessing in sparse arrays - if ( i in arr && arr[ i ] === elem ) { - return i; - } - } - } - - return -1; + return arr == null ? -1 : indexOf.call( arr, elem, i ); }, merge: function( first, second ) { @@ -453,16 +411,8 @@ jQuery.extend({ j = 0, i = first.length; - while ( j < len ) { - first[ i++ ] = second[ j++ ]; - } - - // Support: IE<9 - // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists) - if ( len !== len ) { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; } first.length = i; @@ -491,14 +441,13 @@ jQuery.extend({ // arg is for internal usage only map: function( elems, callback, arg ) { - var value, + var length, value, i = 0, - length = elems.length, - isArray = isArraylike( elems ), ret = []; // Go through the array, translating each of the items to their new values - if ( isArray ) { + if ( isArrayLike( elems ) ) { + length = elems.length; for ( ; i < length; i++ ) { value = callback( elems[ i ], i, arg ); @@ -528,7 +477,7 @@ jQuery.extend({ // Bind a function to a context, optionally partially applying any // arguments. proxy: function( fn, context ) { - var args, proxy, tmp; + var tmp, args, proxy; if ( typeof context === "string" ) { tmp = fn[ context ]; @@ -554,50 +503,55 @@ jQuery.extend({ return proxy; }, - now: function() { - return +( new Date() ); - }, + now: Date.now, // jQuery.support is not used in Core but other projects attach their // properties to it so it needs to exist. support: support -}); +} ); + +// JSHint would error on this code due to the Symbol not being defined in ES5. +// Defining this global in .jshintrc would create a danger of using the global +// unguarded in another place, it seems safer to just disable JSHint for these +// three lines. +/* jshint ignore: start */ +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} +/* jshint ignore: end */ // Populate the class2type map -jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); -}); +} ); -function isArraylike( obj ) { +function isArrayLike( obj ) { // Support: iOS 8.2 (not reproducible in simulator) // `in` check used to prevent JIT error (gh-2145) // hasOwn isn't used here due to false negatives // regarding Nodelist length in IE - var length = "length" in obj && obj.length, + var length = !!obj && "length" in obj && obj.length, type = jQuery.type( obj ); if ( type === "function" || jQuery.isWindow( obj ) ) { return false; } - if ( obj.nodeType === 1 && length ) { - return true; - } - return type === "array" || length === 0 || typeof length === "number" && length > 0 && ( length - 1 ) in obj; } var Sizzle = /*! - * Sizzle CSS Selector Engine v2.2.0-pre + * Sizzle CSS Selector Engine v2.2.1 * http://sizzlejs.com/ * - * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors + * Copyright jQuery Foundation and other contributors * Released under the MIT license * http://jquery.org/license * - * Date: 2014-12-16 + * Date: 2015-10-17 */ (function( window ) { @@ -665,25 +619,21 @@ var i, // Regular expressions - // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + // http://www.w3.org/TR/css3-selectors/#whitespace whitespace = "[\\x20\\t\\r\\n\\f]", - // http://www.w3.org/TR/css3-syntax/#characters - characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", - // Loosely modeled on CSS identifier characters - // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors - // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - identifier = characterEncoding.replace( "w", "w#" ), + // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors - attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace + + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + // Operator (capture 2) "*([*^$|!~]?=)" + whitespace + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + "*\\]", - pseudos = ":(" + characterEncoding + ")(?:\\((" + + pseudos = ":(" + identifier + ")(?:\\((" + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: // 1. quoted (capture 3; capture 4 or capture 5) "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + @@ -706,9 +656,9 @@ var i, ridentifier = new RegExp( "^" + identifier + "$" ), matchExpr = { - "ID": new RegExp( "^#(" + characterEncoding + ")" ), - "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), - "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), "ATTR": new RegExp( "^" + attributes ), "PSEUDO": new RegExp( "^" + pseudos ), "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + @@ -786,103 +736,129 @@ try { } function Sizzle( selector, context, results, seed ) { - var match, elem, m, nodeType, - // QSA vars - i, groups, old, nid, newContext, newSelector; + var m, i, elem, nid, nidselect, match, groups, newSelector, + newContext = context && context.ownerDocument, - if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { - setDocument( context ); - } + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; - context = context || document; results = results || []; - nodeType = context.nodeType; + // Return early from calls with invalid selector or context if ( typeof selector !== "string" || !selector || nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { return results; } - if ( !seed && documentIsHTML ) { + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + + // ID selector + if ( (m = match[1]) ) { + + // Document context + if ( nodeType === 9 ) { + if ( (elem = context.getElementById( m )) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && (elem = newContext.getElementById( m )) && + contains( context, elem ) && + elem.id === m ) { - // Try to shortcut find operations when possible (e.g., not under DocumentFragment) - if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { - // Speed-up: Sizzle("#ID") - if ( (m = match[1]) ) { - if ( nodeType === 9 ) { - elem = context.getElementById( m ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document (jQuery #6963) - if ( elem && elem.parentNode ) { - // Handle the case where IE, Opera, and Webkit return items - // by name instead of ID - if ( elem.id === m ) { results.push( elem ); return results; } - } else { - return results; } - } else { - // Context is not a document - if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && - contains( context, elem ) && elem.id === m ) { - results.push( elem ); - return results; - } - } - // Speed-up: Sizzle("TAG") - } else if ( match[2] ) { - push.apply( results, context.getElementsByTagName( selector ) ); - return results; - - // Speed-up: Sizzle(".CLASS") - } else if ( (m = match[3]) && support.getElementsByClassName ) { - push.apply( results, context.getElementsByClassName( m ) ); - return results; - } - } - - // QSA path - if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { - nid = old = expando; - newContext = context; - newSelector = nodeType !== 1 && selector; - - // qSA works strangely on Element-rooted queries - // We can work around this by specifying an extra ID on the root - // and working up from there (Thanks to Andrew Dupont for the technique) - // IE 8 doesn't work on object elements - if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { - groups = tokenize( selector ); - - if ( (old = context.getAttribute("id")) ) { - nid = old.replace( rescape, "\\$&" ); - } else { - context.setAttribute( "id", nid ); - } - nid = "[id='" + nid + "'] "; - - i = groups.length; - while ( i-- ) { - groups[i] = nid + toSelector( groups[i] ); - } - newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; - newSelector = groups.join(","); - } - - if ( newSelector ) { - try { - push.apply( results, - newContext.querySelectorAll( newSelector ) - ); + // Type selector + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); return results; - } catch(qsaError) { - } finally { - if ( !old ) { - context.removeAttribute("id"); + + // Class selector + } else if ( (m = match[3]) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !compilerCache[ selector + " " ] && + (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + + if ( nodeType !== 1 ) { + newContext = context; + newSelector = selector; + + // qSA looks outside Element context, which is not what we want + // Thanks to Andrew Dupont for this workaround technique + // Support: IE <=8 + // Exclude object elements + } else if ( context.nodeName.toLowerCase() !== "object" ) { + + // Capture the context ID, setting it first if necessary + if ( (nid = context.getAttribute( "id" )) ) { + nid = nid.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", (nid = expando) ); + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + nidselect = ridentifier.test( nid ) ? "#" + nid : "[id='" + nid + "']"; + while ( i-- ) { + groups[i] = nidselect + " " + toSelector( groups[i] ); + } + newSelector = groups.join( "," ); + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } } } } @@ -895,7 +871,7 @@ function Sizzle( selector, context, results, seed ) { /** * Create key-value caches of limited size - * @returns {Function(string, Object)} Returns the Object data after storing it on itself with + * @returns {function(string, object)} Returns the Object data after storing it on itself with * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) * deleting the oldest entry */ @@ -950,7 +926,7 @@ function assert( fn ) { */ function addHandle( attrs, handler ) { var arr = attrs.split("|"), - i = attrs.length; + i = arr.length; while ( i-- ) { Expr.attrHandle[ arr[i] ] = handler; @@ -1063,33 +1039,29 @@ setDocument = Sizzle.setDocument = function( node ) { var hasCompare, parent, doc = node ? node.ownerDocument || node : preferredDoc; - // If no document and documentElement is available, return + // Return early if doc is invalid or already selected if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { return document; } - // Set our document + // Update global variables document = doc; - docElem = doc.documentElement; - parent = doc.defaultView; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); - // Support: IE>8 - // If iframe document is assigned to "document" variable and if iframe has been reloaded, - // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 - // IE6-8 do not support the defaultView property so parent will be undefined - if ( parent && parent !== parent.top ) { - // IE11 does not have attachEvent, so all must suffer + // Support: IE 9-11, Edge + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + if ( (parent = document.defaultView) && parent.top !== parent ) { + // Support: IE 11 if ( parent.addEventListener ) { parent.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only } else if ( parent.attachEvent ) { parent.attachEvent( "onunload", unloadHandler ); } } - /* Support tests - ---------------------------------------------------------------------- */ - documentIsHTML = !isXML( doc ); - /* Attributes ---------------------------------------------------------------------- */ @@ -1106,12 +1078,12 @@ setDocument = Sizzle.setDocument = function( node ) { // Check if getElementsByTagName("*") returns only elements support.getElementsByTagName = assert(function( div ) { - div.appendChild( doc.createComment("") ); + div.appendChild( document.createComment("") ); return !div.getElementsByTagName("*").length; }); // Support: IE<9 - support.getElementsByClassName = rnative.test( doc.getElementsByClassName ); + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); // Support: IE<10 // Check if getElementById returns elements by name @@ -1119,7 +1091,7 @@ setDocument = Sizzle.setDocument = function( node ) { // so use a roundabout getElementsByName test support.getById = assert(function( div ) { docElem.appendChild( div ).id = expando; - return !doc.getElementsByName || !doc.getElementsByName( expando ).length; + return !document.getElementsByName || !document.getElementsByName( expando ).length; }); // ID find and filter @@ -1127,9 +1099,7 @@ setDocument = Sizzle.setDocument = function( node ) { Expr.find["ID"] = function( id, context ) { if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { var m = context.getElementById( id ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - return m && m.parentNode ? [ m ] : []; + return m ? [ m ] : []; } }; Expr.filter["ID"] = function( id ) { @@ -1146,7 +1116,8 @@ setDocument = Sizzle.setDocument = function( node ) { Expr.filter["ID"] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { - var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode("id"); return node && node.value === attrId; }; }; @@ -1186,7 +1157,7 @@ setDocument = Sizzle.setDocument = function( node ) { // Class Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { - if ( documentIsHTML ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { return context.getElementsByClassName( className ); } }; @@ -1206,7 +1177,7 @@ setDocument = Sizzle.setDocument = function( node ) { // See http://bugs.jquery.com/ticket/13378 rbuggyQSA = []; - if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { + if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { // Build QSA regex // Regex strategy adopted from Diego Perini assert(function( div ) { @@ -1216,7 +1187,7 @@ setDocument = Sizzle.setDocument = function( node ) { // since its presence should be enough // http://bugs.jquery.com/ticket/12359 docElem.appendChild( div ).innerHTML = "" + - "" + ""; // Support: IE8, Opera 11-12.16 @@ -1233,7 +1204,7 @@ setDocument = Sizzle.setDocument = function( node ) { rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); } - // Support: Chrome<29, Android<4.2+, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.7+ + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) { rbuggyQSA.push("~="); } @@ -1256,7 +1227,7 @@ setDocument = Sizzle.setDocument = function( node ) { assert(function( div ) { // Support: Windows 8 Native Apps // The type and name attributes are restricted during .innerHTML assignment - var input = doc.createElement("input"); + var input = document.createElement("input"); input.setAttribute( "type", "hidden" ); div.appendChild( input ).setAttribute( "name", "D" ); @@ -1304,7 +1275,7 @@ setDocument = Sizzle.setDocument = function( node ) { hasCompare = rnative.test( docElem.compareDocumentPosition ); // Element contains another - // Purposefully does not implement inclusive descendent + // Purposefully self-exclusive // As in, an element does not contain itself contains = hasCompare || rnative.test( docElem.contains ) ? function( a, b ) { @@ -1358,10 +1329,10 @@ setDocument = Sizzle.setDocument = function( node ) { (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { // Choose the first element that is related to our preferred document - if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { return -1; } - if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { return 1; } @@ -1389,8 +1360,8 @@ setDocument = Sizzle.setDocument = function( node ) { // Parentless nodes are either documents or disconnected if ( !aup || !bup ) { - return a === doc ? -1 : - b === doc ? 1 : + return a === document ? -1 : + b === document ? 1 : aup ? -1 : bup ? 1 : sortInput ? @@ -1427,7 +1398,7 @@ setDocument = Sizzle.setDocument = function( node ) { 0; }; - return doc; + return document; }; Sizzle.matches = function( expr, elements ) { @@ -1444,6 +1415,7 @@ Sizzle.matchesSelector = function( elem, expr ) { expr = expr.replace( rattributeQuotes, "='$1']" ); if ( support.matchesSelector && documentIsHTML && + !compilerCache[ expr + " " ] && ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { @@ -1717,11 +1689,12 @@ Expr = Sizzle.selectors = { } : function( elem, context, xml ) { - var cache, outerCache, node, diff, nodeIndex, start, + var cache, uniqueCache, outerCache, node, nodeIndex, start, dir = simple !== forward ? "nextSibling" : "previousSibling", parent = elem.parentNode, name = ofType && elem.nodeName.toLowerCase(), - useCache = !xml && !ofType; + useCache = !xml && !ofType, + diff = false; if ( parent ) { @@ -1730,7 +1703,10 @@ Expr = Sizzle.selectors = { while ( dir ) { node = elem; while ( (node = node[ dir ]) ) { - if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + return false; } } @@ -1744,11 +1720,21 @@ Expr = Sizzle.selectors = { // non-xml :nth-child(...) stores cache data on `parent` if ( forward && useCache ) { + // Seek `elem` from a previously-cached index - outerCache = parent[ expando ] || (parent[ expando ] = {}); - cache = outerCache[ type ] || []; - nodeIndex = cache[0] === dirruns && cache[1]; - diff = cache[0] === dirruns && cache[2]; + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; node = nodeIndex && parent.childNodes[ nodeIndex ]; while ( (node = ++nodeIndex && node && node[ dir ] || @@ -1758,29 +1744,55 @@ Expr = Sizzle.selectors = { // When found, cache indexes on `parent` and break if ( node.nodeType === 1 && ++diff && node === elem ) { - outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; break; } } - // Use previously-cached element index if available - } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { - diff = cache[1]; - - // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) } else { - // Use the same loop as above to seek `elem` from the start - while ( (node = ++nodeIndex && node && node[ dir ] || - (diff = nodeIndex = 0) || start.pop()) ) { + // Use previously-cached element index if available + if ( useCache ) { + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || (node[ expando ] = {}); - if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { - // Cache the index of each encountered element - if ( useCache ) { - (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; - } + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); - if ( node === elem ) { - break; + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } } } } @@ -2142,10 +2154,10 @@ function addCombinator( matcher, combinator, base ) { // Check against all ancestor/preceding elements function( elem, context, xml ) { - var oldCache, outerCache, + var oldCache, uniqueCache, outerCache, newCache = [ dirruns, doneName ]; - // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching if ( xml ) { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { @@ -2158,14 +2170,19 @@ function addCombinator( matcher, combinator, base ) { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { outerCache = elem[ expando ] || (elem[ expando ] = {}); - if ( (oldCache = outerCache[ dir ]) && + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); + + if ( (oldCache = uniqueCache[ dir ]) && oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { // Assign to newCache so results back-propagate to previous elements return (newCache[ 2 ] = oldCache[ 2 ]); } else { // Reuse newcache so results back-propagate to previous elements - outerCache[ dir ] = newCache; + uniqueCache[ dir ] = newCache; // A match means we're done; a fail means we have to keep checking if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { @@ -2390,18 +2407,21 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) { len = elems.length; if ( outermost ) { - outermostContext = context !== document && context; + outermostContext = context === document || context || outermost; } // Add elements passing elementMatchers directly to results - // Keep `i` a string if there are no elements so `matchedCount` will be "00" below // Support: IE<9, Safari // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id for ( ; i !== len && (elem = elems[i]) != null; i++ ) { if ( byElement && elem ) { j = 0; + if ( !context && elem.ownerDocument !== document ) { + setDocument( elem ); + xml = !documentIsHTML; + } while ( (matcher = elementMatchers[j++]) ) { - if ( matcher( elem, context, xml ) ) { + if ( matcher( elem, context || document, xml) ) { results.push( elem ); break; } @@ -2425,8 +2445,17 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) { } } - // Apply set filters to unmatched elements + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. if ( bySet && i !== matchedCount ) { j = 0; while ( (matcher = setMatchers[j++]) ) { @@ -2518,10 +2547,11 @@ select = Sizzle.select = function( selector, context, results, seed ) { results = results || []; - // Try to minimize operations if there is no seed and only one group + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) if ( match.length === 1 ) { - // Take a shortcut and set the context if the root selector is an ID + // Reduce context if the leading compound selector is an ID tokens = match[0] = match[0].slice( 0 ); if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && support.getById && context.nodeType === 9 && documentIsHTML && @@ -2576,7 +2606,7 @@ select = Sizzle.select = function( selector, context, results, seed ) { context, !documentIsHTML, results, - rsibling.test( selector ) && testContext( context.parentNode ) || context + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context ); return results; }; @@ -2652,17 +2682,46 @@ return Sizzle; jQuery.find = Sizzle; jQuery.expr = Sizzle.selectors; -jQuery.expr[":"] = jQuery.expr.pseudos; -jQuery.unique = Sizzle.uniqueSort; +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; jQuery.text = Sizzle.getText; jQuery.isXMLDoc = Sizzle.isXML; jQuery.contains = Sizzle.contains; +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + var rneedsContext = jQuery.expr.match.needsContext; -var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); +var rsingleTag = ( /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/ ); @@ -2674,14 +2733,14 @@ function winnow( elements, qualifier, not ) { return jQuery.grep( elements, function( elem, i ) { /* jshint -W018 */ return !!qualifier.call( elem, i, elem ) !== not; - }); + } ); } if ( qualifier.nodeType ) { return jQuery.grep( elements, function( elem ) { return ( elem === qualifier ) !== not; - }); + } ); } @@ -2694,8 +2753,8 @@ function winnow( elements, qualifier, not ) { } return jQuery.grep( elements, function( elem ) { - return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not; - }); + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); } jQuery.filter = function( expr, elems, not ) { @@ -2709,24 +2768,24 @@ jQuery.filter = function( expr, elems, not ) { jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { return elem.nodeType === 1; - })); + } ) ); }; -jQuery.fn.extend({ +jQuery.fn.extend( { find: function( selector ) { var i, + len = this.length, ret = [], - self = this, - len = self.length; + self = this; if ( typeof selector !== "string" ) { - return this.pushStack( jQuery( selector ).filter(function() { + return this.pushStack( jQuery( selector ).filter( function() { for ( i = 0; i < len; i++ ) { if ( jQuery.contains( self[ i ], this ) ) { return true; } } - }) ); + } ) ); } for ( i = 0; i < len; i++ ) { @@ -2739,10 +2798,10 @@ jQuery.fn.extend({ return ret; }, filter: function( selector ) { - return this.pushStack( winnow(this, selector || [], false) ); + return this.pushStack( winnow( this, selector || [], false ) ); }, not: function( selector ) { - return this.pushStack( winnow(this, selector || [], true) ); + return this.pushStack( winnow( this, selector || [], true ) ); }, is: function( selector ) { return !!winnow( @@ -2756,7 +2815,7 @@ jQuery.fn.extend({ false ).length; } -}); +} ); // Initialize a jQuery object @@ -2765,15 +2824,12 @@ jQuery.fn.extend({ // A central reference to the root jQuery(document) var rootjQuery, - // Use the correct document accordingly with window argument (sandbox) - document = window.document, - // A simple way to check for HTML strings // Prioritize #id over to avoid XSS via location.hash (#9521) // Strict HTML recognition (#11290: must start with <) rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, - init = jQuery.fn.init = function( selector, context ) { + init = jQuery.fn.init = function( selector, context, root ) { var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false) @@ -2781,9 +2837,16 @@ var rootjQuery, return this; } + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + // Handle HTML strings if ( typeof selector === "string" ) { - if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check match = [ null, selector, null ]; @@ -2792,23 +2855,24 @@ var rootjQuery, } // Match html or make sure no context is specified for #id - if ( match && (match[1] || !context) ) { + if ( match && ( match[ 1 ] || !context ) ) { // HANDLE: $(html) -> $(array) - if ( match[1] ) { - context = context instanceof jQuery ? context[0] : context; + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; - // scripts is true for back-compat + // Option to run scripts is true for back-compat // Intentionally let the error be thrown if parseHTML is not present jQuery.merge( this, jQuery.parseHTML( - match[1], + match[ 1 ], context && context.nodeType ? context.ownerDocument || context : document, true ) ); // HANDLE: $(html, props) - if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { for ( match in context ) { + // Properties of context are called as methods if possible if ( jQuery.isFunction( this[ match ] ) ) { this[ match ]( context[ match ] ); @@ -2824,20 +2888,15 @@ var rootjQuery, // HANDLE: $(#id) } else { - elem = document.getElementById( match[2] ); + elem = document.getElementById( match[ 2 ] ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 + // Support: Blackberry 4.6 + // gEBID returns nodes no longer in the document (#6963) if ( elem && elem.parentNode ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id !== match[2] ) { - return rootjQuery.find( selector ); - } - // Otherwise, we inject the element directly into the jQuery object + // Inject the element directly into the jQuery object this.length = 1; - this[0] = elem; + this[ 0 ] = elem; } this.context = document; @@ -2847,7 +2906,7 @@ var rootjQuery, // HANDLE: $(expr, $(...)) } else if ( !context || context.jquery ) { - return ( context || rootjQuery ).find( selector ); + return ( context || root ).find( selector ); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) @@ -2857,15 +2916,16 @@ var rootjQuery, // HANDLE: $(DOMElement) } else if ( selector.nodeType ) { - this.context = this[0] = selector; + this.context = this[ 0 ] = selector; this.length = 1; return this; // HANDLE: $(function) // Shortcut for document ready } else if ( jQuery.isFunction( selector ) ) { - return typeof rootjQuery.ready !== "undefined" ? - rootjQuery.ready( selector ) : + return root.ready !== undefined ? + root.ready( selector ) : + // Execute immediately if ready is not present selector( jQuery ); } @@ -2886,7 +2946,8 @@ rootjQuery = jQuery( document ); var rparentsprev = /^(?:parents|prev(?:Until|All))/, - // methods guaranteed to produce a unique set when starting from a unique set + + // Methods guaranteed to produce a unique set when starting from a unique set guaranteedUnique = { children: true, contents: true, @@ -2894,46 +2955,19 @@ var rparentsprev = /^(?:parents|prev(?:Until|All))/, prev: true }; -jQuery.extend({ - dir: function( elem, dir, until ) { - var matched = [], - cur = elem[ dir ]; - - while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { - if ( cur.nodeType === 1 ) { - matched.push( cur ); - } - cur = cur[dir]; - } - return matched; - }, - - sibling: function( n, elem ) { - var r = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - r.push( n ); - } - } - - return r; - } -}); - -jQuery.fn.extend({ +jQuery.fn.extend( { has: function( target ) { - var i, - targets = jQuery( target, this ), - len = targets.length; + var targets = jQuery( target, this ), + l = targets.length; - return this.filter(function() { - for ( i = 0; i < len; i++ ) { - if ( jQuery.contains( this, targets[i] ) ) { + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { return true; } } - }); + } ); }, closest: function( selectors, context ) { @@ -2946,14 +2980,15 @@ jQuery.fn.extend({ 0; for ( ; i < l; i++ ) { - for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + // Always skip document fragments - if ( cur.nodeType < 11 && (pos ? - pos.index(cur) > -1 : + if ( cur.nodeType < 11 && ( pos ? + pos.index( cur ) > -1 : // Don't pass non-elements to Sizzle cur.nodeType === 1 && - jQuery.find.matchesSelector(cur, selectors)) ) { + jQuery.find.matchesSelector( cur, selectors ) ) ) { matched.push( cur ); break; @@ -2961,32 +2996,33 @@ jQuery.fn.extend({ } } - return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); }, - // Determine the position of an element within - // the matched set of elements + // Determine the position of an element within the set index: function( elem ) { // No argument, return index in parent if ( !elem ) { - return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1; + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; } - // index in selector + // Index in selector if ( typeof elem === "string" ) { - return jQuery.inArray( this[0], jQuery( elem ) ); + return indexOf.call( jQuery( elem ), this[ 0 ] ); } // Locate the position of the desired element - return jQuery.inArray( + return indexOf.call( this, + // If it receives a jQuery object, the first element is used - elem.jquery ? elem[0] : elem, this ); + elem.jquery ? elem[ 0 ] : elem + ); }, add: function( selector, context ) { return this.pushStack( - jQuery.unique( + jQuery.uniqueSort( jQuery.merge( this.get(), jQuery( selector, context ) ) ) ); @@ -2994,29 +3030,26 @@ jQuery.fn.extend({ addBack: function( selector ) { return this.add( selector == null ? - this.prevObject : this.prevObject.filter(selector) + this.prevObject : this.prevObject.filter( selector ) ); } -}); +} ); function sibling( cur, dir ) { - do { - cur = cur[ dir ]; - } while ( cur && cur.nodeType !== 1 ); - + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} return cur; } -jQuery.each({ +jQuery.each( { parent: function( elem ) { var parent = elem.parentNode; return parent && parent.nodeType !== 11 ? parent : null; }, parents: function( elem ) { - return jQuery.dir( elem, "parentNode" ); + return dir( elem, "parentNode" ); }, parentsUntil: function( elem, i, until ) { - return jQuery.dir( elem, "parentNode", until ); + return dir( elem, "parentNode", until ); }, next: function( elem ) { return sibling( elem, "nextSibling" ); @@ -3025,68 +3058,64 @@ jQuery.each({ return sibling( elem, "previousSibling" ); }, nextAll: function( elem ) { - return jQuery.dir( elem, "nextSibling" ); + return dir( elem, "nextSibling" ); }, prevAll: function( elem ) { - return jQuery.dir( elem, "previousSibling" ); + return dir( elem, "previousSibling" ); }, nextUntil: function( elem, i, until ) { - return jQuery.dir( elem, "nextSibling", until ); + return dir( elem, "nextSibling", until ); }, prevUntil: function( elem, i, until ) { - return jQuery.dir( elem, "previousSibling", until ); + return dir( elem, "previousSibling", until ); }, siblings: function( elem ) { - return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + return siblings( ( elem.parentNode || {} ).firstChild, elem ); }, children: function( elem ) { - return jQuery.sibling( elem.firstChild ); + return siblings( elem.firstChild ); }, contents: function( elem ) { - return jQuery.nodeName( elem, "iframe" ) ? - elem.contentDocument || elem.contentWindow.document : - jQuery.merge( [], elem.childNodes ); + return elem.contentDocument || jQuery.merge( [], elem.childNodes ); } }, function( name, fn ) { jQuery.fn[ name ] = function( until, selector ) { - var ret = jQuery.map( this, fn, until ); + var matched = jQuery.map( this, fn, until ); if ( name.slice( -5 ) !== "Until" ) { selector = until; } if ( selector && typeof selector === "string" ) { - ret = jQuery.filter( selector, ret ); + matched = jQuery.filter( selector, matched ); } if ( this.length > 1 ) { + // Remove duplicates if ( !guaranteedUnique[ name ] ) { - ret = jQuery.unique( ret ); + jQuery.uniqueSort( matched ); } // Reverse order for parents* and prev-derivatives if ( rparentsprev.test( name ) ) { - ret = ret.reverse(); + matched.reverse(); } } - return this.pushStack( ret ); + return this.pushStack( matched ); }; -}); -var rnotwhite = (/\S+/g); +} ); +var rnotwhite = ( /\S+/g ); -// String to Object options format cache -var optionsCache = {}; - -// Convert String-formatted options into Object-formatted ones and store in cache +// Convert String-formatted options into Object-formatted ones function createOptions( options ) { - var object = optionsCache[ options ] = {}; + var object = {}; jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { object[ flag ] = true; - }); + } ); return object; } @@ -3117,156 +3146,186 @@ jQuery.Callbacks = function( options ) { // Convert options from String-formatted to Object-formatted if needed // (we check in cache first) options = typeof options === "string" ? - ( optionsCache[ options ] || createOptions( options ) ) : + createOptions( options ) : jQuery.extend( {}, options ); var // Flag to know if list is currently firing firing, - // Last fire value (for non-forgettable lists) + + // Last fire value for non-forgettable lists memory, + // Flag to know if list was already fired fired, - // End of the loop when firing - firingLength, - // Index of currently firing callback (modified by remove if needed) - firingIndex, - // First callback to fire (used internally by add and fireWith) - firingStart, + + // Flag to prevent firing + locked, + // Actual callback list list = [], - // Stack of fire calls for repeatable lists - stack = !options.once && [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + // Fire callbacks - fire = function( data ) { - memory = options.memory && data; - fired = true; - firingIndex = firingStart || 0; - firingStart = 0; - firingLength = list.length; - firing = true; - for ( ; list && firingIndex < firingLength; firingIndex++ ) { - if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { - memory = false; // To prevent further calls using add - break; + fire = function() { + + // Enforce single-firing + locked = options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } } } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + firing = false; - if ( list ) { - if ( stack ) { - if ( stack.length ) { - fire( stack.shift() ); - } - } else if ( memory ) { + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { list = []; + + // Otherwise, this object is spent } else { - self.disable(); + list = ""; } } }, + // Actual Callbacks object self = { + // Add a callback or a collection of callbacks to the list add: function() { if ( list ) { - // First, we save the current length - var start = list.length; - (function add( args ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { jQuery.each( args, function( _, arg ) { - var type = jQuery.type( arg ); - if ( type === "function" ) { + if ( jQuery.isFunction( arg ) ) { if ( !options.unique || !self.has( arg ) ) { list.push( arg ); } - } else if ( arg && arg.length && type !== "string" ) { + } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) { + // Inspect recursively add( arg ); } - }); - })( arguments ); - // Do we need to add the callbacks to the - // current firing batch? - if ( firing ) { - firingLength = list.length; - // With memory, if we're not firing then - // we should call right away - } else if ( memory ) { - firingStart = start; - fire( memory ); + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); } } return this; }, + // Remove a callback from the list remove: function() { - if ( list ) { - jQuery.each( arguments, function( _, arg ) { - var index; - while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { - list.splice( index, 1 ); - // Handle firing indexes - if ( firing ) { - if ( index <= firingLength ) { - firingLength--; - } - if ( index <= firingIndex ) { - firingIndex--; - } - } + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; } - }); - } + } + } ); return this; }, + // Check if a given callback is in the list. // If no argument is given, return whether or not list has callbacks attached. has: function( fn ) { - return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; }, + // Remove all callbacks from the list empty: function() { - list = []; - firingLength = 0; - return this; - }, - // Have the list do nothing anymore - disable: function() { - list = stack = memory = undefined; - return this; - }, - // Is it disabled? - disabled: function() { - return !list; - }, - // Lock the list in its current state - lock: function() { - stack = undefined; - if ( !memory ) { - self.disable(); + if ( list ) { + list = []; } return this; }, - // Is it locked? - locked: function() { - return !stack; + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + // Call all callbacks with the given context and arguments fireWith: function( context, args ) { - if ( list && ( !fired || stack ) ) { + if ( !locked ) { args = args || []; args = [ context, args.slice ? args.slice() : args ]; - if ( firing ) { - stack.push( args ); - } else { - fire( args ); + queue.push( args ); + if ( !firing ) { + fire(); } } return this; }, + // Call all the callbacks with the given arguments fire: function() { self.fireWith( this, arguments ); return this; }, + // To know if the callbacks have already been called at least once fired: function() { return !!fired; @@ -3277,14 +3336,15 @@ jQuery.Callbacks = function( options ) { }; -jQuery.extend({ +jQuery.extend( { Deferred: function( func ) { var tuples = [ + // action, add listener, listener list, final state - [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], - [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], - [ "notify", "progress", jQuery.Callbacks("memory") ] + [ "resolve", "done", jQuery.Callbacks( "once memory" ), "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), "rejected" ], + [ "notify", "progress", jQuery.Callbacks( "memory" ) ] ], state = "pending", promise = { @@ -3297,25 +3357,30 @@ jQuery.extend({ }, then: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; - return jQuery.Deferred(function( newDefer ) { + return jQuery.Deferred( function( newDefer ) { jQuery.each( tuples, function( i, tuple ) { var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer - deferred[ tuple[1] ](function() { + deferred[ tuple[ 1 ] ]( function() { var returned = fn && fn.apply( this, arguments ); if ( returned && jQuery.isFunction( returned.promise ) ) { returned.promise() + .progress( newDefer.notify ) .done( newDefer.resolve ) - .fail( newDefer.reject ) - .progress( newDefer.notify ); + .fail( newDefer.reject ); } else { - newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + newDefer[ tuple[ 0 ] + "With" ]( + this === promise ? newDefer.promise() : this, + fn ? [ returned ] : arguments + ); } - }); - }); + } ); + } ); fns = null; - }).promise(); + } ).promise(); }, + // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function( obj ) { @@ -3333,11 +3398,12 @@ jQuery.extend({ stateString = tuple[ 3 ]; // promise[ done | fail | progress ] = list.add - promise[ tuple[1] ] = list.add; + promise[ tuple[ 1 ] ] = list.add; // Handle state if ( stateString ) { - list.add(function() { + list.add( function() { + // state = [ resolved | rejected ] state = stateString; @@ -3346,12 +3412,12 @@ jQuery.extend({ } // deferred[ resolve | reject | notify ] - deferred[ tuple[0] ] = function() { - deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? promise : this, arguments ); return this; }; - deferred[ tuple[0] + "With" ] = list.fireWith; - }); + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); // Make the deferred a promise promise.promise( deferred ); @@ -3372,9 +3438,11 @@ jQuery.extend({ length = resolveValues.length, // the count of uncompleted subordinates - remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + remaining = length !== 1 || + ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, - // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + // the master Deferred. + // If resolveValues consist of only a single Deferred, just use that. deferred = remaining === 1 ? subordinate : jQuery.Deferred(), // Update function for both resolve and progress values @@ -3384,8 +3452,7 @@ jQuery.extend({ values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; if ( values === progressValues ) { deferred.notifyWith( contexts, values ); - - } else if ( !(--remaining) ) { + } else if ( !( --remaining ) ) { deferred.resolveWith( contexts, values ); } }; @@ -3393,7 +3460,7 @@ jQuery.extend({ progressValues, progressContexts, resolveContexts; - // add listeners to Deferred subordinates; treat others as resolved + // Add listeners to Deferred subordinates; treat others as resolved if ( length > 1 ) { progressValues = new Array( length ); progressContexts = new Array( length ); @@ -3401,36 +3468,38 @@ jQuery.extend({ for ( ; i < length; i++ ) { if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { resolveValues[ i ].promise() + .progress( updateFunc( i, progressContexts, progressValues ) ) .done( updateFunc( i, resolveContexts, resolveValues ) ) - .fail( deferred.reject ) - .progress( updateFunc( i, progressContexts, progressValues ) ); + .fail( deferred.reject ); } else { --remaining; } } } - // if we're not waiting on anything, resolve the master + // If we're not waiting on anything, resolve the master if ( !remaining ) { deferred.resolveWith( resolveContexts, resolveValues ); } return deferred.promise(); } -}); +} ); // The deferred used on DOM ready var readyList; jQuery.fn.ready = function( fn ) { + // Add the callback jQuery.ready.promise().done( fn ); return this; }; -jQuery.extend({ +jQuery.extend( { + // Is the DOM ready to be used? Set to true once it occurs. isReady: false, @@ -3455,11 +3524,6 @@ jQuery.extend({ return; } - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( !document.body ) { - return setTimeout( jQuery.ready ); - } - // Remember that the DOM is ready jQuery.isReady = true; @@ -3477,31 +3541,15 @@ jQuery.extend({ jQuery( document ).off( "ready" ); } } -}); - -/** - * Clean-up method for dom ready events - */ -function detach() { - if ( document.addEventListener ) { - document.removeEventListener( "DOMContentLoaded", completed, false ); - window.removeEventListener( "load", completed, false ); - - } else { - document.detachEvent( "onreadystatechange", completed ); - window.detachEvent( "onload", completed ); - } -} +} ); /** * The ready event handler and self cleanup method */ function completed() { - // readyState === "complete" is good enough for us to call the dom ready in oldIE - if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) { - detach(); - jQuery.ready(); - } + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); } jQuery.ready.promise = function( obj ) { @@ -3509,163 +3557,323 @@ jQuery.ready.promise = function( obj ) { readyList = jQuery.Deferred(); - // Catch cases where $(document).ready() is called after the browser event has already occurred. - // we once tried to use readyState "interactive" here, but it caused issues like the one - // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 - if ( document.readyState === "complete" ) { + // Catch cases where $(document).ready() is called + // after the browser event has already occurred. + // Support: IE9-10 only + // Older IE sometimes signals "interactive" too soon + if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready - setTimeout( jQuery.ready ); + window.setTimeout( jQuery.ready ); - // Standards-based browsers support DOMContentLoaded - } else if ( document.addEventListener ) { - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", completed, false ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", completed, false ); - - // If IE event model is used } else { - // Ensure firing before onload, maybe late but safe also for iframes - document.attachEvent( "onreadystatechange", completed ); + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); // A fallback to window.onload, that will always work - window.attachEvent( "onload", completed ); - - // If IE and not a frame - // continually check to see if the document is ready - var top = false; - - try { - top = window.frameElement == null && document.documentElement; - } catch(e) {} - - if ( top && top.doScroll ) { - (function doScrollCheck() { - if ( !jQuery.isReady ) { - - try { - // Use the trick by Diego Perini - // http://javascript.nwbox.com/IEContentLoaded/ - top.doScroll("left"); - } catch(e) { - return setTimeout( doScrollCheck, 50 ); - } - - // detach all dom ready events - detach(); - - // and execute any waiting functions - jQuery.ready(); - } - })(); - } + window.addEventListener( "load", completed ); } } return readyList.promise( obj ); }; - -var strundefined = typeof undefined; +// Kick off the DOM ready check even if the user does not +jQuery.ready.promise(); -// Support: IE<9 -// Iteration over object's inherited properties before its own -var i; -for ( i in jQuery( support ) ) { - break; -} -support.ownLast = i !== "0"; -// Note: most support tests are defined in their respective modules. -// false until the test is run -support.inlineBlockNeedsLayout = false; +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; -// Execute ASAP in case we need to set body.style.zoom -jQuery(function() { - // Minified: var a,b,c,d - var val, div, body, container; + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } - body = document.getElementsByTagName( "body" )[ 0 ]; - if ( !body || !body.style ) { - // Return for frameset docs that don't have a body - return; - } + // Sets one value + } else if ( value !== undefined ) { + chainable = true; - // Setup - div = document.createElement( "div" ); - container = document.createElement( "div" ); - container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px"; - body.appendChild( container ).appendChild( div ); + if ( !jQuery.isFunction( value ) ) { + raw = true; + } - if ( typeof div.style.zoom !== strundefined ) { - // Support: IE<8 - // Check if natively block-level elements act like inline-block - // elements when setting their display to 'inline' and giving - // them layout - div.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1"; + if ( bulk ) { - support.inlineBlockNeedsLayout = val = div.offsetWidth === 3; - if ( val ) { - // Prevent IE 6 from affecting layout for positioned elements #11048 - // Prevent IE from shrinking the body in IE 7 mode #12869 - // Support: IE<8 - body.style.zoom = 1; + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } } } - body.removeChild( container ); -}); + return chainable ? + elems : + // Gets + bulk ? + fn.call( elems ) : + len ? fn( elems[ 0 ], key ) : emptyGet; +}; +var acceptData = function( owner ) { - - -(function() { - var div = document.createElement( "div" ); - - // Execute the test only if not already executed in another module. - if (support.deleteExpando == null) { - // Support: IE<9 - support.deleteExpando = true; - try { - delete div.test; - } catch( e ) { - support.deleteExpando = false; - } - } - - // Null elements to avoid leaks in IE. - div = null; -})(); - - -/** - * Determines whether an object can have data - */ -jQuery.acceptData = function( elem ) { - var noData = jQuery.noData[ (elem.nodeName + " ").toLowerCase() ], - nodeType = +elem.nodeType || 1; - - // Do not set data on non-element DOM nodes because it will not be cleared (#8335). - return nodeType !== 1 && nodeType !== 9 ? - false : - - // Nodes accept data unless otherwise specified; rejection can be conditional - !noData || noData !== true && elem.getAttribute("classid") === noData; + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + /* jshint -W018 */ + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); }; + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + register: function( owner, initial ) { + var value = initial || {}; + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable, non-writable property + // configurability must be true to allow the property to be + // deleted with the delete operator + } else { + Object.defineProperty( owner, this.expando, { + value: value, + writable: true, + configurable: true + } ); + } + return owner[ this.expando ]; + }, + cache: function( owner ) { + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( !acceptData( owner ) ) { + return {}; + } + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + if ( typeof data === "string" ) { + cache[ data ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ prop ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + owner[ this.expando ] && owner[ this.expando ][ key ]; + }, + access: function( owner, key, value ) { + var stored; + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + stored = this.get( owner, key ); + + return stored !== undefined ? + stored : this.get( owner, jQuery.camelCase( key ) ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, name, camel, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key === undefined ) { + this.register( owner ); + + } else { + + // Support array or space separated string of keys + if ( jQuery.isArray( key ) ) { + + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = key.concat( key.map( jQuery.camelCase ) ); + } else { + camel = jQuery.camelCase( key ); + + // Try the string as a key before any manipulation + if ( key in cache ) { + name = [ key, camel ]; + } else { + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + name = camel; + name = name in cache ? + [ name ] : ( name.match( rnotwhite ) || [] ); + } + } + + i = name.length; + + while ( i-- ) { + delete cache[ name[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <= 35-45+ + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://code.google.com/p/chromium/issues/detail?id=378607 + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, - rmultiDash = /([A-Z])/g; + rmultiDash = /[A-Z]/g; function dataAttr( elem, key, data ) { + var name; + // If nothing was found internally, try to fetch any // data from the HTML5 data-* attribute if ( data === undefined && elem.nodeType === 1 ) { - - var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); - + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); data = elem.getAttribute( name ); if ( typeof data === "string" ) { @@ -3673,269 +3881,58 @@ function dataAttr( elem, key, data ) { data = data === "true" ? true : data === "false" ? false : data === "null" ? null : + // Only convert to a number if it doesn't change the string +data + "" === data ? +data : rbrace.test( data ) ? jQuery.parseJSON( data ) : data; - } catch( e ) {} + } catch ( e ) {} // Make sure we set the data so it isn't changed later - jQuery.data( elem, key, data ); - + dataUser.set( elem, key, data ); } else { data = undefined; } } - return data; } -// checks a cache object for emptiness -function isEmptyDataObject( obj ) { - var name; - for ( name in obj ) { - - // if the public data object is empty, the private is still empty - if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { - continue; - } - if ( name !== "toJSON" ) { - return false; - } - } - - return true; -} - -function internalData( elem, name, data, pvt /* Internal Use Only */ ) { - if ( !jQuery.acceptData( elem ) ) { - return; - } - - var ret, thisCache, - internalKey = jQuery.expando, - - // We have to handle DOM nodes and JS objects differently because IE6-7 - // can't GC object references properly across the DOM-JS boundary - isNode = elem.nodeType, - - // Only DOM nodes need the global jQuery cache; JS object data is - // attached directly to the object so GC can occur automatically - cache = isNode ? jQuery.cache : elem, - - // Only defining an ID for JS objects if its cache already exists allows - // the code to shortcut on the same path as a DOM node with no cache - id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; - - // Avoid doing any more work than we need to when trying to get data on an - // object that has no data at all - if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) { - return; - } - - if ( !id ) { - // Only DOM nodes need a new unique ID for each element since their data - // ends up in the global cache - if ( isNode ) { - id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++; - } else { - id = internalKey; - } - } - - if ( !cache[ id ] ) { - // Avoid exposing jQuery metadata on plain JS objects when the object - // is serialized using JSON.stringify - cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; - } - - // An object can be passed to jQuery.data instead of a key/value pair; this gets - // shallow copied over onto the existing cache - if ( typeof name === "object" || typeof name === "function" ) { - if ( pvt ) { - cache[ id ] = jQuery.extend( cache[ id ], name ); - } else { - cache[ id ].data = jQuery.extend( cache[ id ].data, name ); - } - } - - thisCache = cache[ id ]; - - // jQuery data() is stored in a separate object inside the object's internal data - // cache in order to avoid key collisions between internal data and user-defined - // data. - if ( !pvt ) { - if ( !thisCache.data ) { - thisCache.data = {}; - } - - thisCache = thisCache.data; - } - - if ( data !== undefined ) { - thisCache[ jQuery.camelCase( name ) ] = data; - } - - // Check for both converted-to-camel and non-converted data property names - // If a data property was specified - if ( typeof name === "string" ) { - - // First Try to find as-is property data - ret = thisCache[ name ]; - - // Test for null|undefined property data - if ( ret == null ) { - - // Try to find the camelCased property - ret = thisCache[ jQuery.camelCase( name ) ]; - } - } else { - ret = thisCache; - } - - return ret; -} - -function internalRemoveData( elem, name, pvt ) { - if ( !jQuery.acceptData( elem ) ) { - return; - } - - var thisCache, i, - isNode = elem.nodeType, - - // See jQuery.data for more information - cache = isNode ? jQuery.cache : elem, - id = isNode ? elem[ jQuery.expando ] : jQuery.expando; - - // If there is already no cache entry for this object, there is no - // purpose in continuing - if ( !cache[ id ] ) { - return; - } - - if ( name ) { - - thisCache = pvt ? cache[ id ] : cache[ id ].data; - - if ( thisCache ) { - - // Support array or space separated string names for data keys - if ( !jQuery.isArray( name ) ) { - - // try the string as a key before any manipulation - if ( name in thisCache ) { - name = [ name ]; - } else { - - // split the camel cased version by spaces unless a key with the spaces exists - name = jQuery.camelCase( name ); - if ( name in thisCache ) { - name = [ name ]; - } else { - name = name.split(" "); - } - } - } else { - // If "name" is an array of keys... - // When data is initially created, via ("key", "val") signature, - // keys will be converted to camelCase. - // Since there is no way to tell _how_ a key was added, remove - // both plain key and camelCase key. #12786 - // This will only penalize the array argument path. - name = name.concat( jQuery.map( name, jQuery.camelCase ) ); - } - - i = name.length; - while ( i-- ) { - delete thisCache[ name[i] ]; - } - - // If there is no data left in the cache, we want to continue - // and let the cache object itself get destroyed - if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) { - return; - } - } - } - - // See jQuery.data for more information - if ( !pvt ) { - delete cache[ id ].data; - - // Don't destroy the parent cache unless the internal data object - // had been the only thing left in it - if ( !isEmptyDataObject( cache[ id ] ) ) { - return; - } - } - - // Destroy the cache - if ( isNode ) { - jQuery.cleanData( [ elem ], true ); - - // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) - /* jshint eqeqeq: false */ - } else if ( support.deleteExpando || cache != cache.window ) { - /* jshint eqeqeq: true */ - delete cache[ id ]; - - // When all else fails, null - } else { - cache[ id ] = null; - } -} - -jQuery.extend({ - cache: {}, - - // The following elements (space-suffixed to avoid Object.prototype collisions) - // throw uncatchable exceptions if you attempt to set expando properties - noData: { - "applet ": true, - "embed ": true, - // ...but Flash objects (which have this classid) *can* handle expandos - "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" - }, - +jQuery.extend( { hasData: function( elem ) { - elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; - return !!elem && !isEmptyDataObject( elem ); + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); }, data: function( elem, name, data ) { - return internalData( elem, name, data ); + return dataUser.access( elem, name, data ); }, removeData: function( elem, name ) { - return internalRemoveData( elem, name ); + dataUser.remove( elem, name ); }, - // For internal use only. + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. _data: function( elem, name, data ) { - return internalData( elem, name, data, true ); + return dataPriv.access( elem, name, data ); }, _removeData: function( elem, name ) { - return internalRemoveData( elem, name, true ); + dataPriv.remove( elem, name ); } -}); +} ); -jQuery.fn.extend({ +jQuery.fn.extend( { data: function( key, value ) { var i, name, data, - elem = this[0], + elem = this[ 0 ], attrs = elem && elem.attributes; - // Special expections of .data basically thwart jQuery.access, - // so implement the relevant behavior ourselves - // Gets all values if ( key === undefined ) { if ( this.length ) { - data = jQuery.data( elem ); + data = dataUser.get( elem ); - if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { i = attrs.length; while ( i-- ) { @@ -3944,12 +3941,12 @@ jQuery.fn.extend({ if ( attrs[ i ] ) { name = attrs[ i ].name; if ( name.indexOf( "data-" ) === 0 ) { - name = jQuery.camelCase( name.slice(5) ); + name = jQuery.camelCase( name.slice( 5 ) ); dataAttr( elem, name, data[ name ] ); } } } - jQuery._data( elem, "parsedAttrs", true ); + dataPriv.set( elem, "hasDataAttrs", true ); } } @@ -3958,43 +3955,96 @@ jQuery.fn.extend({ // Sets multiple values if ( typeof key === "object" ) { - return this.each(function() { - jQuery.data( this, key ); - }); + return this.each( function() { + dataUser.set( this, key ); + } ); } - return arguments.length > 1 ? + return access( this, function( value ) { + var data, camelKey; - // Sets one value - this.each(function() { - jQuery.data( this, key, value ); - }) : + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { - // Gets one value - // Try to fetch any internally stored data first - elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined; + // Attempt to get data from the cache + // with the key as-is + data = dataUser.get( elem, key ) || + + // Try to find dashed key if it exists (gh-2779) + // This is for 2.2.x only + dataUser.get( elem, key.replace( rmultiDash, "-$&" ).toLowerCase() ); + + if ( data !== undefined ) { + return data; + } + + camelKey = jQuery.camelCase( key ); + + // Attempt to get data from the cache + // with the key camelized + data = dataUser.get( elem, camelKey ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, camelKey, undefined ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + camelKey = jQuery.camelCase( key ); + this.each( function() { + + // First, attempt to store a copy or reference of any + // data that might've been store with a camelCased key. + var data = dataUser.get( this, camelKey ); + + // For HTML5 data-* attribute interop, we have to + // store property names with dashes in a camelCase form. + // This might not apply to all properties...* + dataUser.set( this, camelKey, value ); + + // *... In the case of properties that might _actually_ + // have dashes, we need to also store a copy of that + // unchanged property. + if ( key.indexOf( "-" ) > -1 && data !== undefined ) { + dataUser.set( this, key, value ); + } + } ); + }, null, value, arguments.length > 1, null, true ); }, removeData: function( key ) { - return this.each(function() { - jQuery.removeData( this, key ); - }); + return this.each( function() { + dataUser.remove( this, key ); + } ); } -}); +} ); -jQuery.extend({ +jQuery.extend( { queue: function( elem, type, data ) { var queue; if ( elem ) { type = ( type || "fx" ) + "queue"; - queue = jQuery._data( elem, type ); + queue = dataPriv.get( elem, type ); // Speed up dequeue by getting out quickly if this is just a lookup if ( data ) { - if ( !queue || jQuery.isArray(data) ) { - queue = jQuery._data( elem, type, jQuery.makeArray(data) ); + if ( !queue || jQuery.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); } else { queue.push( data ); } @@ -4028,7 +4078,7 @@ jQuery.extend({ queue.unshift( "inprogress" ); } - // clear up the last queue stop function + // Clear up the last queue stop function delete hooks.stop; fn.call( elem, next, hooks ); } @@ -4038,19 +4088,18 @@ jQuery.extend({ } }, - // not intended for public consumption - generates a queueHooks object, or returns the current one + // Not public - generate a queueHooks object, or return the current one _queueHooks: function( elem, type ) { var key = type + "queueHooks"; - return jQuery._data( elem, key ) || jQuery._data( elem, key, { - empty: jQuery.Callbacks("once memory").add(function() { - jQuery._removeData( elem, type + "queue" ); - jQuery._removeData( elem, key ); - }) - }); + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); } -}); +} ); -jQuery.fn.extend({ +jQuery.fn.extend( { queue: function( type, data ) { var setter = 2; @@ -4061,30 +4110,31 @@ jQuery.fn.extend({ } if ( arguments.length < setter ) { - return jQuery.queue( this[0], type ); + return jQuery.queue( this[ 0 ], type ); } return data === undefined ? this : - this.each(function() { + this.each( function() { var queue = jQuery.queue( this, type, data ); - // ensure a hooks for this queue + // Ensure a hooks for this queue jQuery._queueHooks( this, type ); - if ( type === "fx" && queue[0] !== "inprogress" ) { + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { jQuery.dequeue( this, type ); } - }); + } ); }, dequeue: function( type ) { - return this.each(function() { + return this.each( function() { jQuery.dequeue( this, type ); - }); + } ); }, clearQueue: function( type ) { return this.queue( type || "fx", [] ); }, + // Get a promise resolved when queues of a certain type // are emptied (fx is the type by default) promise: function( type, obj ) { @@ -4106,7 +4156,7 @@ jQuery.fn.extend({ type = type || "fx"; while ( i-- ) { - tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); if ( tmp && tmp.empty ) { count++; tmp.empty.add( resolve ); @@ -4115,171 +4165,267 @@ jQuery.fn.extend({ resolve(); return defer.promise( obj ); } -}); -var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; var isHidden = function( elem, el ) { + // isHidden might be called from jQuery#filter function; // in that case, element will be second argument elem = el || elem; - return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); + return jQuery.css( elem, "display" ) === "none" || + !jQuery.contains( elem.ownerDocument, elem ); }; -// Multifunctional method to get and set values of a collection -// The value/s can optionally be executed if it's a function -var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) { +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, + scale = 1, + maxIterations = 20, + currentValue = tween ? + function() { return tween.cur(); } : + function() { return jQuery.css( elem, prop, "" ); }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + do { + + // If previous iteration zeroed out, double until we get *something*. + // Use string for doubling so we don't accidentally see scale as unchanged below + scale = scale || ".5"; + + // Adjust and apply + initialInUnit = initialInUnit / scale; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Update scale, tolerating zero or NaN from tween.cur() + // Break the loop if scale is unchanged or perfect, or if we've just had enough. + } while ( + scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations + ); + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([\w:-]+)/ ); + +var rscriptType = ( /^$|\/(?:java|ecma)script/i ); + + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // Support: IE9 + option: [ 1, "" ], + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "
                  " ], + col: [ 2, "", "
                  " ], + tr: [ 2, "", "
                  " ], + td: [ 3, "", "
                  " ], + + _default: [ 0, "", "" ] +}; + +// Support: IE9 +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + + +function getAll( context, tag ) { + + // Support: IE9-11+ + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( tag || "*" ) : + typeof context.querySelectorAll !== "undefined" ? + context.querySelectorAll( tag || "*" ) : + []; + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], ret ) : + ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { var i = 0, - length = elems.length, - bulk = key == null; + l = elems.length; - // Sets many values - if ( jQuery.type( key ) === "object" ) { - chainable = true; - for ( i in key ) { - jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); - } + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} - // Sets one value - } else if ( value !== undefined ) { - chainable = true; - if ( !jQuery.isFunction( value ) ) { - raw = true; - } +var rhtml = /<|&#?\w+;/; - if ( bulk ) { - // Bulk operations run against the entire set - if ( raw ) { - fn.call( elems, value ); - fn = null; +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, contains, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; - // ...except when executing function values + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + + // Support: Android<4.1, PhantomJS<2 + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes } else { - bulk = fn; - fn = function( elem, key, value ) { - return bulk.call( jQuery( elem ), value ); - }; - } - } + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); - if ( fn ) { - for ( ; i < length; i++ ) { - fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android<4.1, PhantomJS<2 + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; } } } - return chainable ? - elems : + // Remove wrapper from fragment + fragment.textContent = ""; - // Gets - bulk ? - fn.call( elems ) : - length ? fn( elems[0], key ) : emptyGet; -}; -var rcheckableType = (/^(?:checkbox|radio)$/i); + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); -(function() { - // Minified: var a,b,c - var input = document.createElement( "input" ), - div = document.createElement( "div" ), - fragment = document.createDocumentFragment(); + // Support: Android 4.0-4.3, Safari<=5.1 + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); - // Setup - div.innerHTML = "
                  a"; + div.appendChild( input ); - // IE strips leading whitespace when .innerHTML is used - support.leadingWhitespace = div.firstChild.nodeType === 3; - - // Make sure that tbody elements aren't automatically inserted - // IE will insert them into empty tables - support.tbody = !div.getElementsByTagName( "tbody" ).length; - - // Make sure that link elements get serialized correctly by innerHTML - // This requires a wrapper element in IE - support.htmlSerialize = !!div.getElementsByTagName( "link" ).length; - - // Makes sure cloning an html5 element does not cause problems - // Where outerHTML is undefined, this still works - support.html5Clone = - document.createElement( "nav" ).cloneNode( true ).outerHTML !== "<:nav>"; - - // Check if a disconnected checkbox will retain its checked - // value of true after appended to the DOM (IE6/7) - input.type = "checkbox"; - input.checked = true; - fragment.appendChild( input ); - support.appendChecked = input.checked; - - // Make sure textarea (and checkbox) defaultValue is properly cloned - // Support: IE6-IE11+ - div.innerHTML = ""; - support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; - - // #11217 - WebKit loses check when the name is after the checked attribute - fragment.appendChild( div ); - div.innerHTML = ""; - - // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 - // old WebKit doesn't clone checked state correctly in fragments + // Support: Safari<=5.1, Android<4.2 + // Older WebKit doesn't clone checked state correctly in fragments support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; - // Support: IE<9 - // Opera does not clone events (and typeof div.attachEvent === undefined). - // IE9-10 clones events bound via attachEvent, but they don't trigger with .click() - support.noCloneEvent = true; - if ( div.attachEvent ) { - div.attachEvent( "onclick", function() { - support.noCloneEvent = false; - }); - - div.cloneNode( true ).click(); - } - - // Execute the test only if not already executed in another module. - if (support.deleteExpando == null) { - // Support: IE<9 - support.deleteExpando = true; - try { - delete div.test; - } catch( e ) { - support.deleteExpando = false; - } - } -})(); + // Support: IE<=11+ + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +} )(); -(function() { - var i, eventName, - div = document.createElement( "div" ); - - // Support: IE<9 (lack submit/change bubble), Firefox 23+ (lack focusin event) - for ( i in { submit: true, change: true, focusin: true }) { - eventName = "on" + i; - - if ( !(support[ i + "Bubbles" ] = eventName in window) ) { - // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) - div.setAttribute( eventName, "t" ); - support[ i + "Bubbles" ] = div.attributes[ eventName ].expando === false; - } - } - - // Null elements to avoid leaks in IE. - div = null; -})(); - - -var rformElems = /^(?:input|select|textarea)$/i, +var rkeyEvent = /^key/, - rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/, - rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; function returnTrue() { return true; @@ -4289,12 +4435,75 @@ function returnFalse() { return false; } +// Support: IE9 +// See #13393 for more info function safeActiveElement() { try { return document.activeElement; } catch ( err ) { } } +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + /* * Helper functions for managing events -- not part of the public interface. * Props to Dean Edwards' addEvent library for many of the ideas. @@ -4304,10 +4513,11 @@ jQuery.event = { global: {}, add: function( elem, types, handler, data, selector ) { - var tmp, events, t, handleObjIn, - special, eventHandle, handleObj, - handlers, type, namespaces, origType, - elemData = jQuery._data( elem ); + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); // Don't attach events to noData or text/comment nodes (but allow plain objects) if ( !elemData ) { @@ -4327,28 +4537,26 @@ jQuery.event = { } // Init the element's event structure and main handler, if this is the first - if ( !(events = elemData.events) ) { + if ( !( events = elemData.events ) ) { events = elemData.events = {}; } - if ( !(eventHandle = elemData.handle) ) { + if ( !( eventHandle = elemData.handle ) ) { eventHandle = elemData.handle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and // when an event is called after a page has unloaded - return typeof jQuery !== strundefined && (!e || jQuery.event.triggered !== e.type) ? - jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : - undefined; + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; }; - // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events - eventHandle.elem = elem; } // Handle multiple events separated by a space types = ( types || "" ).match( rnotwhite ) || [ "" ]; t = types.length; while ( t-- ) { - tmp = rtypenamespace.exec( types[t] ) || []; - type = origType = tmp[1]; - namespaces = ( tmp[2] || "" ).split( "." ).sort(); + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); // There *must* be a type, no attaching namespace-only handlers if ( !type ) { @@ -4365,7 +4573,7 @@ jQuery.event = { special = jQuery.event.special[ type ] || {}; // handleObj is passed to all event handlers - handleObj = jQuery.extend({ + handleObj = jQuery.extend( { type: type, origType: origType, data: data, @@ -4373,22 +4581,20 @@ jQuery.event = { guid: handler.guid, selector: selector, needsContext: selector && jQuery.expr.match.needsContext.test( selector ), - namespace: namespaces.join(".") + namespace: namespaces.join( "." ) }, handleObjIn ); // Init the event handler queue if we're the first - if ( !(handlers = events[ type ]) ) { + if ( !( handlers = events[ type ] ) ) { handlers = events[ type ] = []; handlers.delegateCount = 0; - // Only use addEventListener/attachEvent if the special events handler returns false - if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - // Bind the global event handler to the element - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle, false ); + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - } else if ( elem.attachEvent ) { - elem.attachEvent( "on" + type, eventHandle ); + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); } } } @@ -4412,19 +4618,17 @@ jQuery.event = { jQuery.event.global[ type ] = true; } - // Nullify elem to prevent memory leaks in IE - elem = null; }, // Detach an event or set of events from an element remove: function( elem, types, handler, selector, mappedTypes ) { - var j, handleObj, tmp, - origCount, t, events, - special, handlers, type, - namespaces, origType, - elemData = jQuery.hasData( elem ) && jQuery._data( elem ); - if ( !elemData || !(events = elemData.events) ) { + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { return; } @@ -4432,9 +4636,9 @@ jQuery.event = { types = ( types || "" ).match( rnotwhite ) || [ "" ]; t = types.length; while ( t-- ) { - tmp = rtypenamespace.exec( types[t] ) || []; - type = origType = tmp[1]; - namespaces = ( tmp[2] || "" ).split( "." ).sort(); + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); // Unbind all events (on this namespace, if provided) for the element if ( !type ) { @@ -4447,7 +4651,8 @@ jQuery.event = { special = jQuery.event.special[ type ] || {}; type = ( selector ? special.delegateType : special.bindType ) || type; handlers = events[ type ] || []; - tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); // Remove matching events origCount = j = handlers.length; @@ -4457,7 +4662,8 @@ jQuery.event = { if ( ( mappedTypes || origType === handleObj.origType ) && ( !handler || handler.guid === handleObj.guid ) && ( !tmp || tmp.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { handlers.splice( j, 1 ); if ( handleObj.selector ) { @@ -4472,7 +4678,9 @@ jQuery.event = { // Remove generic event handler if we removed something and no more handlers exist // (avoids potential for endless recursion during removal of special event handlers) if ( origCount && !handlers.length ) { - if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); } @@ -4480,167 +4688,25 @@ jQuery.event = { } } - // Remove the expando if it's no longer used + // Remove data and the expando if it's no longer used if ( jQuery.isEmptyObject( events ) ) { - delete elemData.handle; - - // removeData also checks for emptiness and clears the expando if empty - // so use it instead of delete - jQuery._removeData( elem, "events" ); + dataPriv.remove( elem, "handle events" ); } }, - trigger: function( event, data, elem, onlyHandlers ) { - var handle, ontype, cur, - bubbleType, special, tmp, i, - eventPath = [ elem || document ], - type = hasOwn.call( event, "type" ) ? event.type : event, - namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; - - cur = tmp = elem = elem || document; - - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf(".") >= 0 ) { - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split("."); - type = namespaces.shift(); - namespaces.sort(); - } - ontype = type.indexOf(":") < 0 && "on" + type; - - // Caller can pass in a jQuery.Event object, Object, or just an event type string - event = event[ jQuery.expando ] ? - event : - new jQuery.Event( type, typeof event === "object" && event ); - - // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) - event.isTrigger = onlyHandlers ? 2 : 3; - event.namespace = namespaces.join("."); - event.namespace_re = event.namespace ? - new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : - null; - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data == null ? - [ event ] : - jQuery.makeArray( data, [ event ] ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (#9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) - if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - if ( !rfocusMorph.test( bubbleType + type ) ) { - cur = cur.parentNode; - } - for ( ; cur; cur = cur.parentNode ) { - eventPath.push( cur ); - tmp = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( tmp === (elem.ownerDocument || document) ) { - eventPath.push( tmp.defaultView || tmp.parentWindow || window ); - } - } - - // Fire handlers on the event path - i = 0; - while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { - - event.type = i > 1 ? - bubbleType : - special.bindType || type; - - // jQuery handler - handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - - // Native handler - handle = ontype && cur[ ontype ]; - if ( handle && handle.apply && jQuery.acceptData( cur ) ) { - event.result = handle.apply( cur, data ); - if ( event.result === false ) { - event.preventDefault(); - } - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && - jQuery.acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name name as the event. - // Can't use an .isFunction() check here because IE6/7 fails that test. - // Don't do default actions on window, that's where global variables be (#6170) - if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - tmp = elem[ ontype ]; - - if ( tmp ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - try { - elem[ type ](); - } catch ( e ) { - // IE<9 dies on focus/blur to hidden element (#1486,#12518) - // only reproducible on winXP IE8 native, not IE9 in IE8 mode - } - jQuery.event.triggered = undefined; - - if ( tmp ) { - elem[ ontype ] = tmp; - } - } - } - } - - return event.result; - }, - dispatch: function( event ) { // Make a writable jQuery.Event from the native event object event = jQuery.event.fix( event ); - var i, ret, handleObj, matched, j, + var i, j, ret, matched, handleObj, handlerQueue = [], args = slice.call( arguments ), - handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], + handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [], special = jQuery.event.special[ event.type ] || {}; // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[0] = event; + args[ 0 ] = event; event.delegateTarget = this; // Call the preDispatch hook for the mapped type, and let it bail if desired @@ -4653,24 +4719,25 @@ jQuery.event = { // Run delegates first; they may want to stop propagation beneath us i = 0; - while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { event.currentTarget = matched.elem; j = 0; - while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { - // Triggered event must either 1) have no namespace, or - // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). - if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { + // Triggered event must either 1) have no namespace, or 2) have namespace(s) + // a subset or equal to those in the bound event (both can have no namespace). + if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) { event.handleObj = handleObj; event.data = handleObj.data; - ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) - .apply( matched.elem, args ); + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); if ( ret !== undefined ) { - if ( (event.result = ret) === false ) { + if ( ( event.result = ret ) === false ) { event.preventDefault(); event.stopPropagation(); } @@ -4688,23 +4755,25 @@ jQuery.event = { }, handlers: function( event, handlers ) { - var sel, handleObj, matches, i, + var i, matches, sel, handleObj, handlerQueue = [], delegateCount = handlers.delegateCount, cur = event.target; + // Support (at least): Chrome, IE9 // Find delegate handlers // Black-hole SVG instance trees (#13180) - // Avoid non-left-click bubbling in Firefox (#3861) - if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { + // + // Support: Firefox<=42+ + // Avoid non-left-click in FF but don't block IE radio events (#3861, gh-2343) + if ( delegateCount && cur.nodeType && + ( event.type !== "click" || isNaN( event.button ) || event.button < 1 ) ) { - /* jshint eqeqeq: false */ - for ( ; cur != this; cur = cur.parentNode || this ) { - /* jshint eqeqeq: true */ + for ( ; cur !== this; cur = cur.parentNode || this ) { // Don't check non-elements (#13208) // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) - if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) { + if ( cur.nodeType === 1 && ( cur.disabled !== true || event.type !== "click" ) ) { matches = []; for ( i = 0; i < delegateCount; i++ ) { handleObj = handlers[ i ]; @@ -4714,7 +4783,7 @@ jQuery.event = { if ( matches[ sel ] === undefined ) { matches[ sel ] = handleObj.needsContext ? - jQuery( sel, this ).index( cur ) >= 0 : + jQuery( sel, this ).index( cur ) > -1 : jQuery.find( sel, this, null, [ cur ] ).length; } if ( matches[ sel ] ) { @@ -4722,7 +4791,7 @@ jQuery.event = { } } if ( matches.length ) { - handlerQueue.push({ elem: cur, handlers: matches }); + handlerQueue.push( { elem: cur, handlers: matches } ); } } } @@ -4730,12 +4799,62 @@ jQuery.event = { // Add the remaining (directly-bound) handlers if ( delegateCount < handlers.length ) { - handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); + handlerQueue.push( { elem: this, handlers: handlers.slice( delegateCount ) } ); } return handlerQueue; }, + // Includes some event props shared by KeyEvent and MouseEvent + props: ( "altKey bubbles cancelable ctrlKey currentTarget detail eventPhase " + + "metaKey relatedTarget shiftKey target timeStamp view which" ).split( " " ), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split( " " ), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: ( "button buttons clientX clientY offsetX offsetY pageX pageY " + + "screenX screenY toElement" ).split( " " ), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - + ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - + ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + fix: function( event ) { if ( event[ jQuery.expando ] ) { return event; @@ -4763,92 +4882,34 @@ jQuery.event = { event[ prop ] = originalEvent[ prop ]; } - // Support: IE<9 - // Fix target property (#1925) + // Support: Cordova 2.5 (WebKit) (#13255) + // All events should have a target; Cordova deviceready doesn't if ( !event.target ) { - event.target = originalEvent.srcElement || document; + event.target = document; } - // Support: Chrome 23+, Safari? + // Support: Safari 6.0+, Chrome<28 // Target should not be a text node (#504, #13143) if ( event.target.nodeType === 3 ) { event.target = event.target.parentNode; } - // Support: IE<9 - // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) - event.metaKey = !!event.metaKey; - return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; }, - // Includes some event props shared by KeyEvent and MouseEvent - props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), - - fixHooks: {}, - - keyHooks: { - props: "char charCode key keyCode".split(" "), - filter: function( event, original ) { - - // Add which for key events - if ( event.which == null ) { - event.which = original.charCode != null ? original.charCode : original.keyCode; - } - - return event; - } - }, - - mouseHooks: { - props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), - filter: function( event, original ) { - var body, eventDoc, doc, - button = original.button, - fromElement = original.fromElement; - - // Calculate pageX/Y if missing and clientX/Y available - if ( event.pageX == null && original.clientX != null ) { - eventDoc = event.target.ownerDocument || document; - doc = eventDoc.documentElement; - body = eventDoc.body; - - event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); - event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); - } - - // Add relatedTarget, if necessary - if ( !event.relatedTarget && fromElement ) { - event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - // Note: button is not normalized, so don't use it - if ( !event.which && button !== undefined ) { - event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); - } - - return event; - } - }, - special: { load: { + // Prevent triggered image.load events from bubbling to window.load noBubble: true }, focus: { + // Fire native event if possible so blur/focus sequence is correct trigger: function() { if ( this !== safeActiveElement() && this.focus ) { - try { - this.focus(); - return false; - } catch ( e ) { - // Support: IE<9 - // If we error on focus to hidden element (#1486, #12518), - // let .trigger() run the handlers - } + this.focus(); + return false; } }, delegateType: "focusin" @@ -4863,9 +4924,10 @@ jQuery.event = { delegateType: "focusout" }, click: { + // For checkbox, fire native event so checked state will be right trigger: function() { - if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { + if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) { this.click(); return false; } @@ -4887,56 +4949,21 @@ jQuery.event = { } } } - }, - - simulate: function( type, elem, event, bubble ) { - // Piggyback on a donor event to simulate a different one. - // Fake originalEvent to avoid donor's stopPropagation, but if the - // simulated event prevents default then we do the same on the donor. - var e = jQuery.extend( - new jQuery.Event(), - event, - { - type: type, - isSimulated: true, - originalEvent: {} - } - ); - if ( bubble ) { - jQuery.event.trigger( e, null, elem ); - } else { - jQuery.event.dispatch.call( elem, e ); - } - if ( e.isDefaultPrevented() ) { - event.preventDefault(); - } } }; -jQuery.removeEvent = document.removeEventListener ? - function( elem, type, handle ) { - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle, false ); - } - } : - function( elem, type, handle ) { - var name = "on" + type; +jQuery.removeEvent = function( elem, type, handle ) { - if ( elem.detachEvent ) { - - // #8545, #7054, preventing memory leaks for custom events in IE6-8 - // detachEvent needed property on element, by name of that event, to properly expose it to GC - if ( typeof elem[ name ] === strundefined ) { - elem[ name ] = null; - } - - elem.detachEvent( name, handle ); - } - }; + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword - if ( !(this instanceof jQuery.Event) ) { + if ( !( this instanceof jQuery.Event ) ) { return new jQuery.Event( src, props ); } @@ -4949,7 +4976,8 @@ jQuery.Event = function( src, props ) { // by a handler lower down the tree; reflect the correct value. this.isDefaultPrevented = src.defaultPrevented || src.defaultPrevented === undefined && - // Support: IE < 9, Android < 4.0 + + // Support: Android<4.0 src.returnValue === false ? returnTrue : returnFalse; @@ -4974,50 +5002,36 @@ jQuery.Event = function( src, props ) { // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html jQuery.Event.prototype = { + constructor: jQuery.Event, isDefaultPrevented: returnFalse, isPropagationStopped: returnFalse, isImmediatePropagationStopped: returnFalse, + isSimulated: false, preventDefault: function() { var e = this.originalEvent; this.isDefaultPrevented = returnTrue; - if ( !e ) { - return; - } - // If preventDefault exists, run it on the original event - if ( e.preventDefault ) { + if ( e && !this.isSimulated ) { e.preventDefault(); - - // Support: IE - // Otherwise set the returnValue property of the original event to false - } else { - e.returnValue = false; } }, stopPropagation: function() { var e = this.originalEvent; this.isPropagationStopped = returnTrue; - if ( !e ) { - return; - } - // If stopPropagation exists, run it on the original event - if ( e.stopPropagation ) { + + if ( e && !this.isSimulated ) { e.stopPropagation(); } - - // Support: IE - // Set the cancelBubble property of the original event to true - e.cancelBubble = true; }, stopImmediatePropagation: function() { var e = this.originalEvent; this.isImmediatePropagationStopped = returnTrue; - if ( e && e.stopImmediatePropagation ) { + if ( e && !this.isSimulated ) { e.stopImmediatePropagation(); } @@ -5026,7 +5040,14 @@ jQuery.Event.prototype = { }; // Create mouseenter/leave events using mouseover/out and event-time checks -jQuery.each({ +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://code.google.com/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { mouseenter: "mouseover", mouseleave: "mouseout", pointerenter: "pointerover", @@ -5042,9 +5063,9 @@ jQuery.each({ related = event.relatedTarget, handleObj = event.handleObj; - // For mousenter/leave call the handler if related is outside the target. + // For mouseenter/leave call the handler if related is outside the target. // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { event.type = handleObj.origType; ret = handleObj.handler.apply( this, arguments ); event.type = fix; @@ -5052,219 +5073,32 @@ jQuery.each({ return ret; } }; -}); +} ); -// IE submit delegation -if ( !support.submitBubbles ) { - - jQuery.event.special.submit = { - setup: function() { - // Only need this for delegated form submit events - if ( jQuery.nodeName( this, "form" ) ) { - return false; - } - - // Lazy-add a submit handler when a descendant form may potentially be submitted - jQuery.event.add( this, "click._submit keypress._submit", function( e ) { - // Node name check avoids a VML-related crash in IE (#9807) - var elem = e.target, - form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; - if ( form && !jQuery._data( form, "submitBubbles" ) ) { - jQuery.event.add( form, "submit._submit", function( event ) { - event._submit_bubble = true; - }); - jQuery._data( form, "submitBubbles", true ); - } - }); - // return undefined since we don't need an event listener - }, - - postDispatch: function( event ) { - // If form was submitted by the user, bubble the event up the tree - if ( event._submit_bubble ) { - delete event._submit_bubble; - if ( this.parentNode && !event.isTrigger ) { - jQuery.event.simulate( "submit", this.parentNode, event, true ); - } - } - }, - - teardown: function() { - // Only need this for delegated form submit events - if ( jQuery.nodeName( this, "form" ) ) { - return false; - } - - // Remove delegated handlers; cleanData eventually reaps submit handlers attached above - jQuery.event.remove( this, "._submit" ); - } - }; -} - -// IE change delegation and checkbox/radio fix -if ( !support.changeBubbles ) { - - jQuery.event.special.change = { - - setup: function() { - - if ( rformElems.test( this.nodeName ) ) { - // IE doesn't fire change on a check/radio until blur; trigger it on click - // after a propertychange. Eat the blur-change in special.change.handle. - // This still fires onchange a second time for check/radio after blur. - if ( this.type === "checkbox" || this.type === "radio" ) { - jQuery.event.add( this, "propertychange._change", function( event ) { - if ( event.originalEvent.propertyName === "checked" ) { - this._just_changed = true; - } - }); - jQuery.event.add( this, "click._change", function( event ) { - if ( this._just_changed && !event.isTrigger ) { - this._just_changed = false; - } - // Allow triggered, simulated change events (#11500) - jQuery.event.simulate( "change", this, event, true ); - }); - } - return false; - } - // Delegated event; lazy-add a change handler on descendant inputs - jQuery.event.add( this, "beforeactivate._change", function( e ) { - var elem = e.target; - - if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) { - jQuery.event.add( elem, "change._change", function( event ) { - if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { - jQuery.event.simulate( "change", this.parentNode, event, true ); - } - }); - jQuery._data( elem, "changeBubbles", true ); - } - }); - }, - - handle: function( event ) { - var elem = event.target; - - // Swallow native change events from checkbox/radio, we already triggered them above - if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { - return event.handleObj.handler.apply( this, arguments ); - } - }, - - teardown: function() { - jQuery.event.remove( this, "._change" ); - - return !rformElems.test( this.nodeName ); - } - }; -} - -// Create "bubbling" focus and blur events -if ( !support.focusinBubbles ) { - jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler on the document while someone wants focusin/focusout - var handler = function( event ) { - jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); - }; - - jQuery.event.special[ fix ] = { - setup: function() { - var doc = this.ownerDocument || this, - attaches = jQuery._data( doc, fix ); - - if ( !attaches ) { - doc.addEventListener( orig, handler, true ); - } - jQuery._data( doc, fix, ( attaches || 0 ) + 1 ); - }, - teardown: function() { - var doc = this.ownerDocument || this, - attaches = jQuery._data( doc, fix ) - 1; - - if ( !attaches ) { - doc.removeEventListener( orig, handler, true ); - jQuery._removeData( doc, fix ); - } else { - jQuery._data( doc, fix, attaches ); - } - } - }; - }); -} - -jQuery.fn.extend({ - - on: function( types, selector, data, fn, /*INTERNAL*/ one ) { - var type, origFn; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - this.on( type, selector, data, types[ type ], one ); - } - return this; - } - - if ( data == null && fn == null ) { - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return this; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return this.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - }); +jQuery.fn.extend( { + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); }, one: function( types, selector, data, fn ) { - return this.on( types, selector, data, fn, 1 ); + return on( this, types, selector, data, fn, 1 ); }, off: function( types, selector, fn ) { var handleObj, type; if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event handleObj = types.handleObj; jQuery( types.delegateTarget ).off( - handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, handleObj.selector, handleObj.handler ); return this; } if ( typeof types === "object" ) { + // ( types-object [, selector] ) for ( type in types ) { this.off( type, selector, types[ type ] ); @@ -5272,6 +5106,7 @@ jQuery.fn.extend({ return this; } if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) fn = selector; selector = undefined; @@ -5279,263 +5114,232 @@ jQuery.fn.extend({ if ( fn === false ) { fn = returnFalse; } - return this.each(function() { + return this.each( function() { jQuery.event.remove( this, types, fn, selector ); - }); - }, - - trigger: function( type, data ) { - return this.each(function() { - jQuery.event.trigger( type, data, this ); - }); - }, - triggerHandler: function( type, data ) { - var elem = this[0]; - if ( elem ) { - return jQuery.event.trigger( type, data, elem, true ); - } + } ); } -}); +} ); -function createSafeFragment( document ) { - var list = nodeNames.split( "|" ), - safeFrag = document.createDocumentFragment(); +var + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi, - if ( safeFrag.createElement ) { - while ( list.length ) { - safeFrag.createElement( - list.pop() - ); - } - } - return safeFrag; -} + // Support: IE 10-11, Edge 10240+ + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /]", "i"), - rleadingWhitespace = /^\s+/, - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, - rtagName = /<([\w:]+)/, - rtbody = /\s*$/g, + rcleanScript = /^\s*\s*$/g; - // We have to close these tags to support XHTML (#13200) - wrapMap = { - option: [ 1, "" ], - legend: [ 1, "
                  ", "
                  " ], - area: [ 1, "", "" ], - param: [ 1, "", "" ], - thead: [ 1, "", "
                  " ], - tr: [ 2, "", "
                  " ], - col: [ 2, "", "
                  " ], - td: [ 3, "", "
                  " ], - - // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, - // unless wrapped in a div with non-breaking characters in front of it. - _default: support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
                  ", "
                  " ] - }, - safeFragment = createSafeFragment( document ), - fragmentDiv = safeFragment.appendChild( document.createElement("div") ); - -wrapMap.optgroup = wrapMap.option; -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -function getAll( context, tag ) { - var elems, elem, - i = 0, - found = typeof context.getElementsByTagName !== strundefined ? context.getElementsByTagName( tag || "*" ) : - typeof context.querySelectorAll !== strundefined ? context.querySelectorAll( tag || "*" ) : - undefined; - - if ( !found ) { - for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) { - if ( !tag || jQuery.nodeName( elem, tag ) ) { - found.push( elem ); - } else { - jQuery.merge( found, getAll( elem, tag ) ); - } - } - } - - return tag === undefined || tag && jQuery.nodeName( context, tag ) ? - jQuery.merge( [ context ], found ) : - found; -} - -// Used in buildFragment, fixes the defaultChecked property -function fixDefaultChecked( elem ) { - if ( rcheckableType.test( elem.type ) ) { - elem.defaultChecked = elem.checked; - } -} - -// Support: IE<8 // Manipulating tables requires a tbody function manipulationTarget( elem, content ) { return jQuery.nodeName( elem, "table" ) && jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? - elem.getElementsByTagName("tbody")[0] || - elem.appendChild( elem.ownerDocument.createElement("tbody") ) : + elem.getElementsByTagName( "tbody" )[ 0 ] || + elem.appendChild( elem.ownerDocument.createElement( "tbody" ) ) : elem; } // Replace/restore the type attribute of script elements for safe DOM manipulation function disableScript( elem ) { - elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type; + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; return elem; } function restoreScript( elem ) { var match = rscriptTypeMasked.exec( elem.type ); + if ( match ) { - elem.type = match[1]; + elem.type = match[ 1 ]; } else { - elem.removeAttribute("type"); + elem.removeAttribute( "type" ); } + return elem; } -// Mark scripts as having already been evaluated -function setGlobalEval( elems, refElements ) { - var elem, - i = 0; - for ( ; (elem = elems[i]) != null; i++ ) { - jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) ); - } -} - function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; - if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { - return; - } - - var type, i, l, - oldData = jQuery._data( src ), - curData = jQuery._data( dest, oldData ), - events = oldData.events; - - if ( events ) { - delete curData.handle; - curData.events = {}; - - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } - - // make the cloned public data object a copy from the original - if ( curData.data ) { - curData.data = jQuery.extend( {}, curData.data ); - } -} - -function fixCloneNodeIssues( src, dest ) { - var nodeName, e, data; - - // We do not need to do anything for non-Elements if ( dest.nodeType !== 1 ) { return; } - nodeName = dest.nodeName.toLowerCase(); + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.access( src ); + pdataCur = dataPriv.set( dest, pdataOld ); + events = pdataOld.events; - // IE6-8 copies events bound via attachEvent when using cloneNode. - if ( !support.noCloneEvent && dest[ jQuery.expando ] ) { - data = jQuery._data( dest ); + if ( events ) { + delete pdataCur.handle; + pdataCur.events = {}; - for ( e in data.events ) { - jQuery.removeEvent( dest, e, data.handle ); + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } } - - // Event data gets referenced instead of copied if the expando gets copied too - dest.removeAttribute( jQuery.expando ); } - // IE blanks contents when cloning scripts, and tries to evaluate newly-set text - if ( nodeName === "script" && dest.text !== src.text ) { - disableScript( dest ).text = src.text; - restoreScript( dest ); + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); - // IE6-10 improperly clones children of object elements using classid. - // IE10 throws NoModificationAllowedError if parent is null, #12132. - } else if ( nodeName === "object" ) { - if ( dest.parentNode ) { - dest.outerHTML = src.outerHTML; - } + dataUser.set( dest, udataCur ); + } +} - // This path appears unavoidable for IE9. When cloning an object - // element in IE9, the outerHTML strategy above is not sufficient. - // If the src has innerHTML and the destination does not, - // copy the src.innerHTML into the dest.innerHTML. #10324 - if ( support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) { - dest.innerHTML = src.innerHTML; - } +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); - } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) { - // IE6-8 fails to persist the checked state of a cloned checkbox - // or radio button. Worse, IE6-7 fail to give the cloned element - // a checked appearance if the defaultChecked value isn't also set + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; - dest.defaultChecked = dest.checked = src.checked; - - // IE6-7 get confused and end up setting the value of a cloned - // checkbox/radio button to an empty string instead of "on" - if ( dest.value !== src.value ) { - dest.value = src.value; - } - - // IE6-8 fails to return the selected option to the default selected - // state when cloning options - } else if ( nodeName === "option" ) { - dest.defaultSelected = dest.selected = src.defaultSelected; - - // IE6-8 fails to set the defaultValue to the correct value when - // cloning other types of input fields + // Fails to return the selected option to the default selected state when cloning options } else if ( nodeName === "input" || nodeName === "textarea" ) { dest.defaultValue = src.defaultValue; } } -jQuery.extend({ - clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var destElements, node, clone, i, srcElements, - inPage = jQuery.contains( elem.ownerDocument, elem ); +function domManip( collection, args, callback, ignored ) { - if ( support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { - clone = elem.cloneNode( true ); + // Flatten any nested arrays + args = concat.apply( [], args ); - // IE<=8 does not properly clone detached, unknown element nodes - } else { - fragmentDiv.innerHTML = elem.outerHTML; - fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( isFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; } - if ( (!support.noCloneEvent || !support.noCloneChecked) && - (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android<4.1, PhantomJS<2 + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html.replace( rxhtmlTag, "<$1>" ); + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = jQuery.contains( elem.ownerDocument, elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 destElements = getAll( clone ); srcElements = getAll( elem ); - // Fix all IE cloning issues - for ( i = 0; (node = srcElements[i]) != null; ++i ) { - // Ensure that the destination node is not null; Fixes #9587 - if ( destElements[i] ) { - fixCloneNodeIssues( node, destElements[i] ); - } + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); } } @@ -5545,8 +5349,8 @@ jQuery.extend({ srcElements = srcElements || getAll( elem ); destElements = destElements || getAll( clone ); - for ( i = 0; (node = srcElements[i]) != null; i++ ) { - cloneCopyEvent( node, destElements[i] ); + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); } } else { cloneCopyEvent( elem, clone ); @@ -5559,154 +5363,18 @@ jQuery.extend({ setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); } - destElements = srcElements = node = null; - // Return the cloned set return clone; }, - buildFragment: function( elems, context, scripts, selection ) { - var j, elem, contains, - tmp, tag, tbody, wrap, - l = elems.length, - - // Ensure a safe fragment - safe = createSafeFragment( context ), - - nodes = [], + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, i = 0; - for ( ; i < l; i++ ) { - elem = elems[ i ]; - - if ( elem || elem === 0 ) { - - // Add nodes directly - if ( jQuery.type( elem ) === "object" ) { - jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); - - // Convert non-html into a text node - } else if ( !rhtml.test( elem ) ) { - nodes.push( context.createTextNode( elem ) ); - - // Convert html into DOM nodes - } else { - tmp = tmp || safe.appendChild( context.createElement("div") ); - - // Deserialize a standard representation - tag = (rtagName.exec( elem ) || [ "", "" ])[ 1 ].toLowerCase(); - wrap = wrapMap[ tag ] || wrapMap._default; - - tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[2]; - - // Descend through wrappers to the right content - j = wrap[0]; - while ( j-- ) { - tmp = tmp.lastChild; - } - - // Manually add leading whitespace removed by IE - if ( !support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { - nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); - } - - // Remove IE's autoinserted from table fragments - if ( !support.tbody ) { - - // String was a , *may* have spurious - elem = tag === "table" && !rtbody.test( elem ) ? - tmp.firstChild : - - // String was a bare or - wrap[1] === "
                  " && !rtbody.test( elem ) ? - tmp : - 0; - - j = elem && elem.childNodes.length; - while ( j-- ) { - if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) { - elem.removeChild( tbody ); - } - } - } - - jQuery.merge( nodes, tmp.childNodes ); - - // Fix #12392 for WebKit and IE > 9 - tmp.textContent = ""; - - // Fix #12392 for oldIE - while ( tmp.firstChild ) { - tmp.removeChild( tmp.firstChild ); - } - - // Remember the top-level container for proper cleanup - tmp = safe.lastChild; - } - } - } - - // Fix #11356: Clear elements from fragment - if ( tmp ) { - safe.removeChild( tmp ); - } - - // Reset defaultChecked for any radios and checkboxes - // about to be appended to the DOM in IE 6/7 (#8060) - if ( !support.appendChecked ) { - jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); - } - - i = 0; - while ( (elem = nodes[ i++ ]) ) { - - // #4087 - If origin and destination elements are the same, and this is - // that element, do not do anything - if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { - continue; - } - - contains = jQuery.contains( elem.ownerDocument, elem ); - - // Append to fragment - tmp = getAll( safe.appendChild( elem ), "script" ); - - // Preserve script evaluation history - if ( contains ) { - setGlobalEval( tmp ); - } - - // Capture executables - if ( scripts ) { - j = 0; - while ( (elem = tmp[ j++ ]) ) { - if ( rscriptType.test( elem.type || "" ) ) { - scripts.push( elem ); - } - } - } - } - - tmp = null; - - return safe; - }, - - cleanData: function( elems, /* internal */ acceptData ) { - var elem, type, id, data, - i = 0, - internalKey = jQuery.expando, - cache = jQuery.cache, - deleteExpando = support.deleteExpando, - special = jQuery.event.special; - - for ( ; (elem = elems[i]) != null; i++ ) { - if ( acceptData || jQuery.acceptData( elem ) ) { - - id = elem[ internalKey ]; - data = id && cache[ id ]; - - if ( data ) { + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { if ( data.events ) { for ( type in data.events ) { if ( special[ type ] ) { @@ -5719,116 +5387,92 @@ jQuery.extend({ } } - // Remove cache only if it was not already removed by jQuery.event.remove - if ( cache[ id ] ) { + // Support: Chrome <= 35-45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { - delete cache[ id ]; - - // IE does not allow us to delete expando properties from nodes, - // nor does it have a removeAttribute function on Document nodes; - // we must handle all of these cases - if ( deleteExpando ) { - delete elem[ internalKey ]; - - } else if ( typeof elem.removeAttribute !== strundefined ) { - elem.removeAttribute( internalKey ); - - } else { - elem[ internalKey ] = null; - } - - deletedIds.push( id ); - } + // Support: Chrome <= 35-45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; } } } } -}); +} ); + +jQuery.fn.extend( { + + // Keep domManip exposed until 3.0 (gh-2225) + domManip: domManip, + + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, -jQuery.fn.extend({ text: function( value ) { return access( this, function( value ) { return value === undefined ? jQuery.text( this ) : - this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); }, null, value, arguments.length ); }, append: function() { - return this.domManip( arguments, function( elem ) { + return domManip( this, arguments, function( elem ) { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { var target = manipulationTarget( this, elem ); target.appendChild( elem ); } - }); + } ); }, prepend: function() { - return this.domManip( arguments, function( elem ) { + return domManip( this, arguments, function( elem ) { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { var target = manipulationTarget( this, elem ); target.insertBefore( elem, target.firstChild ); } - }); + } ); }, before: function() { - return this.domManip( arguments, function( elem ) { + return domManip( this, arguments, function( elem ) { if ( this.parentNode ) { this.parentNode.insertBefore( elem, this ); } - }); + } ); }, after: function() { - return this.domManip( arguments, function( elem ) { + return domManip( this, arguments, function( elem ) { if ( this.parentNode ) { this.parentNode.insertBefore( elem, this.nextSibling ); } - }); - }, - - remove: function( selector, keepData /* Internal Use Only */ ) { - var elem, - elems = selector ? jQuery.filter( selector, this ) : this, - i = 0; - - for ( ; (elem = elems[i]) != null; i++ ) { - - if ( !keepData && elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem ) ); - } - - if ( elem.parentNode ) { - if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { - setGlobalEval( getAll( elem, "script" ) ); - } - elem.parentNode.removeChild( elem ); - } - } - - return this; + } ); }, empty: function() { var elem, i = 0; - for ( ; (elem = this[i]) != null; i++ ) { - // Remove element nodes and prevent memory leaks + for ( ; ( elem = this[ i ] ) != null; i++ ) { if ( elem.nodeType === 1 ) { + + // Prevent memory leaks jQuery.cleanData( getAll( elem, false ) ); - } - // Remove any remaining nodes - while ( elem.firstChild ) { - elem.removeChild( elem.firstChild ); - } - - // If this is a select, ensure that it displays empty (#12336) - // Support: IE<9 - if ( elem.options && jQuery.nodeName( elem, "select" ) ) { - elem.options.length = 0; + // Remove any remaining nodes + elem.textContent = ""; } } @@ -5839,9 +5483,9 @@ jQuery.fn.extend({ dataAndEvents = dataAndEvents == null ? false : dataAndEvents; deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - return this.map(function() { + return this.map( function() { return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); - }); + } ); }, html: function( value ) { @@ -5850,24 +5494,21 @@ jQuery.fn.extend({ i = 0, l = this.length; - if ( value === undefined ) { - return elem.nodeType === 1 ? - elem.innerHTML.replace( rinlinejQuery, "" ) : - undefined; + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; } // See if we can take a shortcut and just use innerHTML if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - ( support.htmlSerialize || !rnoshimcache.test( value ) ) && - ( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && - !wrapMap[ (rtagName.exec( value ) || [ "", "" ])[ 1 ].toLowerCase() ] ) { + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { - value = value.replace( rxhtmlTag, "<$1>" ); + value = jQuery.htmlPrefilter( value ); try { - for (; i < l; i++ ) { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + // Remove element nodes and prevent memory leaks - elem = this[i] || {}; if ( elem.nodeType === 1 ) { jQuery.cleanData( getAll( elem, false ) ); elem.innerHTML = value; @@ -5877,7 +5518,7 @@ jQuery.fn.extend({ elem = 0; // If using innerHTML throws an exception, use the fallback method - } catch(e) {} + } catch ( e ) {} } if ( elem ) { @@ -5887,117 +5528,25 @@ jQuery.fn.extend({ }, replaceWith: function() { - var arg = arguments[ 0 ]; + var ignored = []; - // Make the changes, replacing each context element with the new content - this.domManip( arguments, function( elem ) { - arg = this.parentNode; + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; - jQuery.cleanData( getAll( this ) ); - - if ( arg ) { - arg.replaceChild( elem, this ); - } - }); - - // Force removal if there was no new content (e.g., from empty arguments) - return arg && (arg.length || arg.nodeType) ? this : this.remove(); - }, - - detach: function( selector ) { - return this.remove( selector, true ); - }, - - domManip: function( args, callback ) { - - // Flatten any nested arrays - args = concat.apply( [], args ); - - var first, node, hasScripts, - scripts, doc, fragment, - i = 0, - l = this.length, - set = this, - iNoClone = l - 1, - value = args[0], - isFunction = jQuery.isFunction( value ); - - // We can't cloneNode fragments that contain checked, in WebKit - if ( isFunction || - ( l > 1 && typeof value === "string" && - !support.checkClone && rchecked.test( value ) ) ) { - return this.each(function( index ) { - var self = set.eq( index ); - if ( isFunction ) { - args[0] = value.call( this, index, self.html() ); + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); } - self.domManip( args, callback ); - }); - } - - if ( l ) { - fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); - first = fragment.firstChild; - - if ( fragment.childNodes.length === 1 ) { - fragment = first; } - if ( first ) { - scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); - hasScripts = scripts.length; - - // Use the original fragment for the last item instead of the first because it can end up - // being emptied incorrectly in certain situations (#8070). - for ( ; i < l; i++ ) { - node = fragment; - - if ( i !== iNoClone ) { - node = jQuery.clone( node, true, true ); - - // Keep references to cloned scripts for later restoration - if ( hasScripts ) { - jQuery.merge( scripts, getAll( node, "script" ) ); - } - } - - callback.call( this[i], node, i ); - } - - if ( hasScripts ) { - doc = scripts[ scripts.length - 1 ].ownerDocument; - - // Reenable scripts - jQuery.map( scripts, restoreScript ); - - // Evaluate executable scripts on first document insertion - for ( i = 0; i < hasScripts; i++ ) { - node = scripts[ i ]; - if ( rscriptType.test( node.type || "" ) && - !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { - - if ( node.src ) { - // Optional AJAX dependency, but won't run scripts if not present - if ( jQuery._evalUrl ) { - jQuery._evalUrl( node.src ); - } - } else { - jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) ); - } - } - } - } - - // Fix #11809: Avoid leaking memory - fragment = first = null; - } - } - - return this; + // Force callback invocation + }, ignored ); } -}); +} ); -jQuery.each({ +jQuery.each( { appendTo: "append", prependTo: "prepend", insertBefore: "before", @@ -6006,43 +5555,45 @@ jQuery.each({ }, function( name, original ) { jQuery.fn[ name ] = function( selector ) { var elems, - i = 0, ret = [], insert = jQuery( selector ), - last = insert.length - 1; + last = insert.length - 1, + i = 0; for ( ; i <= last; i++ ) { - elems = i === last ? this : this.clone(true); - jQuery( insert[i] )[ original ]( elems ); + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); - // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() + // Support: QtWebKit + // .get() because push.apply(_, arraylike) throws push.apply( ret, elems.get() ); } return this.pushStack( ret ); }; -}); +} ); var iframe, - elemdisplay = {}; + elemdisplay = { + + // Support: Firefox + // We have to pre-define these values for FF (#10227) + HTML: "block", + BODY: "block" + }; /** * Retrieve the actual display of a element * @param {String} name nodeName of the element * @param {Object} doc Document object */ + // Called only from within defaultDisplay function actualDisplay( name, doc ) { - var style, - elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), + var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), - // getDefaultComputedStyle might be reliably used only on attached element - display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? - - // Use of this method is a temporary fix (more like optmization) until something better comes along, - // since it was removed from specification and supported only in FF - style.display : jQuery.css( elem[ 0 ], "display" ); + display = jQuery.css( elem[ 0 ], "display" ); // We don't have any data stored on the element, // so use "detach" method as fast way to get rid of the element @@ -6066,10 +5617,11 @@ function defaultDisplay( nodeName ) { if ( display === "none" || !display ) { // Use the already-created iframe if possible - iframe = (iframe || jQuery( "