diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 000000000..3899c241c
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "web/api/app/Plugin/Crud"]
+ path = web/api/app/Plugin/Crud
+ url = https://github.com/FriendsOfCake/crud.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c3834ce01..1ec3a619d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,8 +4,10 @@
#
cmake_minimum_required (VERSION 2.6)
project (zoneminder)
-set(zoneminder_VERSION "1.28.1")
+set(zoneminder_VERSION "1.28.100")
set(ZM_ENGINE_VERSION 29)
+# make API version a minor of ZM version
+set(zoneminder_API_VERSION "${zoneminder_VERSION}.1")
# CMake does not allow out-of-source build if CMakeCache.exists
# in the source folder. Abort and notify the user
@@ -30,6 +32,27 @@ endif(NOT CMAKE_BUILD_TYPE)
#set(CMAKE_VERBOSE_MAKEFILE ON)
#set(CMAKE_INSTALL_ALWAYS ON)
+# Host OS Check
+set(HOST_OS "")
+if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+ set(HOST_OS "linux")
+endif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+if(${CMAKE_SYSTEM_NAME} MATCHES ".*(SunOS|Solaris).*")
+ set(HOST_OS "solaris")
+ set(SOLARIS 1)
+endif(${CMAKE_SYSTEM_NAME} MATCHES ".*(SunOS|Solaris).*")
+if(${CMAKE_SYSTEM_NAME} MATCHES ".*BSD.*")
+ set(HOST_OS "BSD")
+ set(BSD 1)
+endif(${CMAKE_SYSTEM_NAME} MATCHES ".*BSD.*")
+if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
+ set(HOST_OS "darwin")
+endif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
+if(NOT HOST_OS)
+ message(FATAL_ERROR
+ "ZoneMinder was unable to deterimine the host OS. Please report this. Value of CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}")
+endif(NOT HOST_OS)
+
# Default CLFAGS and CXXFLAGS:
set(CMAKE_C_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2")
set(CMAKE_CXX_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2")
@@ -185,27 +208,6 @@ include_directories("${CMAKE_BINARY_DIR}")
# This is required to enable searching in lib64 (if exists), do not change
set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS ON)
-# Host OS Check
-set(HOST_OS "")
-if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
- set(HOST_OS "linux")
-endif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
-if(${CMAKE_SYSTEM_NAME} MATCHES ".*(SunOS|Solaris).*")
- set(HOST_OS "solaris")
- set(SOLARIS 1)
-endif(${CMAKE_SYSTEM_NAME} MATCHES ".*(SunOS|Solaris).*")
-if(${CMAKE_SYSTEM_NAME} MATCHES ".*BSD.*")
- set(HOST_OS "BSD")
- set(BSD 1)
-endif(${CMAKE_SYSTEM_NAME} MATCHES ".*BSD.*")
-if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
- set(HOST_OS "darwin")
-endif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
-if(NOT HOST_OS)
- message(FATAL_ERROR
- "ZoneMinder was unable to deterimine the host OS. Please report this. Value of CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}")
-endif(NOT HOST_OS)
-
# System checks
check_include_file("libv4l1-videodev.h" HAVE_LIBV4L1_VIDEODEV_H)
if(NOT HAVE_LIBV4L1_VIDEODEV_H)
@@ -226,6 +228,16 @@ check_type_size("ucontext_t" HAVE_UCONTEXT_T)
# *** LIBRARY CHECKS ***
+if (UNIX)
+ include (CheckLibraryExists)
+ CHECK_LIBRARY_EXISTS(rt clock_gettime "time.h" HAVE_CLOCK_GETTIME)
+ if(NOT HAVE_CLOCK_GETTIME)
+ message(FATAL_ERROR "clock_gettime not found")
+ else(NOT HAVE_CLOCK_GETTIME)
+ list(APPEND ZM_BIN_LIBS "-lrt")
+ endif(NOT HAVE_CLOCK_GETTIME)
+endif(UNIX)
+
# zlib
find_package(ZLIB)
if(ZLIB_FOUND)
@@ -404,7 +416,7 @@ if(NOT ZM_NO_FFMPEG)
set(HAVE_LIBAVFORMAT 1)
list(APPEND ZM_BIN_LIBS "${AVFORMAT_LIBRARIES}")
set(LIBS "${LIBS} -lavformat")
- find_path(AVFORMAT_INCLUDE_DIR "libavformat/avformat.h")
+ 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}")
@@ -422,7 +434,7 @@ if(NOT ZM_NO_FFMPEG)
set(HAVE_LIBAVCODEC 1)
list(APPEND ZM_BIN_LIBS "${AVCODEC_LIBRARIES}")
set(LIBS "${LIBS} -lavcodec")
- find_path(AVCODEC_INCLUDE_DIR "libavcodec/avcodec.h")
+ 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}")
@@ -440,7 +452,7 @@ if(NOT ZM_NO_FFMPEG)
set(HAVE_LIBAVDEVICE 1)
list(APPEND ZM_BIN_LIBS "${AVDEVICE_LIBRARIES}")
set(LIBS "${LIBS} -lavdevice")
- find_path(AVDEVICE_INCLUDE_DIR "libavdevice/avdevice.h")
+ 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}")
@@ -458,7 +470,7 @@ if(NOT ZM_NO_FFMPEG)
set(HAVE_LIBAVUTIL 1)
list(APPEND ZM_BIN_LIBS "${AVUTIL_LIBRARIES}")
set(LIBS "${LIBS} -lavutil")
- find_path(AVUTIL_INCLUDE_DIR "libavutil/avutil.h")
+ 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}")
@@ -477,7 +489,7 @@ if(NOT ZM_NO_FFMPEG)
set(HAVE_LIBSWSCALE 1)
list(APPEND ZM_BIN_LIBS "${SWSCALE_LIBRARIES}")
set(LIBS "${LIBS} -lswscale")
- find_path(SWSCALE_INCLUDE_DIR "libswscale/swscale.h")
+ 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}")
@@ -490,7 +502,9 @@ if(NOT ZM_NO_FFMPEG)
endif(SWSCALE_LIBRARIES)
# Find the path to the ffmpeg executable
- find_program(FFMPEG_EXECUTABLE ffmpeg PATH_SUFFIXES ffmpeg)
+ find_program(FFMPEG_EXECUTABLE
+ NAMES ffmpeg avconv
+ PATH_SUFFIXES ffmpeg)
if(FFMPEG_EXECUTABLE)
set(PATH_FFMPEG "${FFMPEG_EXECUTABLE}")
set(OPT_FFMPEG "yes")
@@ -666,6 +680,7 @@ endif(NOT POLKIT_FOUND)
set(ZM_PID "${ZM_RUNDIR}/zm.pid")
set(ZM_CONFIG "${ZM_CONFIG_DIR}/zm.conf")
set(VERSION "${zoneminder_VERSION}")
+set(API_VERSION "${zoneminder_API_VERSION}")
set(PKGDATADIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/zoneminder")
set(BINDIR "${CMAKE_INSTALL_FULL_BINDIR}")
set(LIBDIR "${CMAKE_INSTALL_FULL_LIBDIR}")
@@ -687,6 +702,10 @@ configure_file(zoneminder-config.cmake "${CMAKE_CURRENT_BINARY_DIR}/config.h" @O
configure_file(zmconfgen.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmconfgen.pl" @ONLY)
configure_file(zmlinkcontent.sh.in "${CMAKE_CURRENT_BINARY_DIR}/zmlinkcontent.sh" @ONLY)
+# Create a target for man pages
+include(Pod2Man)
+ADD_MANPAGE_TARGET()
+
# Process subdirectories
add_subdirectory(src)
add_subdirectory(scripts)
diff --git a/Dockerfile b/Dockerfile
index 52b018efd..382330a09 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -11,9 +11,12 @@ RUN apt-get update && apt-get install -y \
libpolkit-gobject-1-dev build-essential libmysqlclient-dev libssl-dev libbz2-dev libpcre3-dev \
libdbi-perl libarchive-zip-perl libdate-manip-perl libdevice-serialport-perl libmime-perl libpcre3 \
libwww-perl libdbd-mysql-perl libsys-mmap-perl yasm automake autoconf libjpeg-turbo8-dev \
- libjpeg-turbo8 libtheora-dev libvorbis-dev libvpx-dev libx264-dev libmp4v2-dev ffmpeg mysql-client \
+ libjpeg-turbo8 libtheora-dev libvorbis-dev libvpx-dev libx264-dev libmp4v2-dev libav-tools mysql-client \
apache2 php5 php5-mysql apache2-mpm-prefork libapache2-mod-php5 php5-cli openssh-server \
- mysql-server libvlc-dev libvlc5 libvlccore-dev libvlccore5 vlc-data vlc libcurl4-openssl-dev
+ mysql-server libvlc-dev libvlc5 libvlccore-dev libvlccore5 vlc-data libcurl4-openssl-dev \
+ libavformat-dev libswscale-dev libavutil-dev libavcodec-dev libavfilter-dev \
+ libavresample-dev libavdevice-dev libpostproc-dev libv4l-dev libtool libnetpbm10-dev \
+ libmime-lite-perl dh-autoreconf dpatch
# Copy local code into our container
ADD . /ZoneMinder
diff --git a/cmake/Modules/FindPolkit.cmake b/cmake/Modules/FindPolkit.cmake
index 62b386a90..cd7903176 100644
--- a/cmake/Modules/FindPolkit.cmake
+++ b/cmake/Modules/FindPolkit.cmake
@@ -19,7 +19,7 @@
# use pkg-config to get the directories and then use these values
# in the FIND_PATH() and FIND_LIBRARY() calls
find_package(PkgConfig)
- pkg_check_modules(PC_POLKIT polkit-gobject-1)
+ pkg_search_module(PC_POLKIT polkit-gobject-1 polkit)
#pkg_check_modules(PC_POLKIT_AGENT polkit-agent-1)
set(POLKIT_DEFINITIONS ${PC_POLKIT_CFLAGS_OTHER})
endif (NOT WIN32)
@@ -31,8 +31,8 @@
HINTS ${PC_POLKIT_INCLUDE_DIRS}
)
find_path( POLKIT_INCLUDE_DIR
- NAMES polkit/polkit.h
- PATH_SUFFIXES polkit-1
+ NAMES polkit/polkit.h libpolkit/libpolkit.h
+ PATH_SUFFIXES polkit-1 polkit
HINTS ${PC_POLKIT_INCLUDE_DIRS}
)
#find_path( POLKIT_AGENT_INCLUDE_DIR
@@ -43,7 +43,7 @@
#set(POLKIT_INCLUDE_DIRS ${GLIB2_INCLUDE_DIR} ${_POLKIT_INCLUDE_DIR})
#set(POLKIT_AGENT_INCLUDE_DIRS ${GLIB2_INCLUDE_DIR} ${_POLKIT_AGENT_INCLUDE_DIR})
find_library( POLKIT_LIBRARIES
- NAMES polkit-gobject-1
+ NAMES polkit-gobject-1 polkit
HINTS ${PC_POLKIT_LIBDIR}
)
#find_library( POLKIT_AGENT_LIBRARY
diff --git a/cmake/Modules/Pod2Man.cmake b/cmake/Modules/Pod2Man.cmake
new file mode 100644
index 000000000..6804fab53
--- /dev/null
+++ b/cmake/Modules/Pod2Man.cmake
@@ -0,0 +1,71 @@
+#
+# Copyright (C) 2012 Emmanuel Roullit
+#
+# 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 St, Fifth Floor, Boston, MA 02110, USA
+#
+
+# Generate man pages of the project by using the
+# POD header written in the tool source code.
+# To use it, include this file in CMakeLists.txt and
+# invoke POD2MAN()
+
+MACRO(POD2MAN PODFILE MANFILE SECTION)
+ FIND_PROGRAM(POD2MAN pod2man)
+ FIND_PROGRAM(GZIP gzip)
+
+ IF(NOT POD2MAN)
+ MESSAGE(FATAL ERROR "Need pod2man installed to generate man page")
+ ENDIF(NOT POD2MAN)
+
+ IF(NOT GZIP)
+ MESSAGE(FATAL ERROR "Need gzip installed to compress man page")
+ ENDIF(NOT GZIP)
+
+ IF(NOT EXISTS ${PODFILE})
+ MESSAGE(FATAL ERROR "Could not find pod file ${PODFILE} to generate man page")
+ ENDIF(NOT EXISTS ${PODFILE})
+
+ ADD_CUSTOM_COMMAND(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}
+ DEPENDS ${PODFILE}
+ COMMAND ${POD2MAN}
+ ARGS --section ${SECTION} --center ${CMAKE_PROJECT_NAME} --release --stderr --name ${MANFILE}
+ ${PODFILE} > ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}
+ )
+
+ ADD_CUSTOM_COMMAND(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}.gz
+ COMMAND ${GZIP} --best -c ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION} > ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}.gz
+ DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}
+ )
+
+ SET(MANPAGE_TARGET "man-${MANFILE}")
+
+ ADD_CUSTOM_TARGET(${MANPAGE_TARGET} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}.gz)
+ ADD_DEPENDENCIES(man ${MANPAGE_TARGET})
+
+ INSTALL(
+ FILES ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}.gz
+ DESTINATION share/man/man${SECTION}
+ )
+ENDMACRO(POD2MAN PODFILE MANFILE SECTION)
+
+MACRO(ADD_MANPAGE_TARGET)
+ # It is not possible add a dependency to target 'install'
+ # Run hard-coded 'make man' when 'make install' is invoked
+ INSTALL(CODE "EXECUTE_PROCESS(COMMAND make man)")
+ ADD_CUSTOM_TARGET(man)
+ENDMACRO(ADD_MANPAGE_TARGET)
+
diff --git a/configure.ac b/configure.ac
index 156d92f16..40e4ea9fb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,9 @@
+#
+# Building ZoneMinder via Autotools will be depreciated soon
+# For instructions on building with cmake, please see INSTALL
+#
AC_PREREQ(2.59)
-AC_INIT(zm,1.28.1,[http://www.zoneminder.com/forums/ - Please check FAQ first],zoneminder,http://www.zoneminder.com/downloads.html)
+AC_INIT(zm,1.28.100,[http://www.zoneminder.com/forums/ - Please check FAQ first],zoneminder,http://www.zoneminder.com/downloads.html)
AC_CONFIG_AUX_DIR([.])
AM_INIT_AUTOMAKE
LT_PREREQ([2.4.2])
diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in
index 57cd9a2a0..52bcf2b7d 100644
--- a/db/zm_create.sql.in
+++ b/db/zm_create.sql.in
@@ -315,6 +315,7 @@ DROP TABLE IF EXISTS `Monitors`;
CREATE TABLE `Monitors` (
`Id` int(10) unsigned NOT NULL auto_increment,
`Name` varchar(64) NOT NULL default '',
+ `ServerId` int(10) unsigned,
`Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL') NOT NULL default 'Local',
`Function` enum('None','Monitor','Modect','Record','Mocord','Nodect') NOT NULL default 'Monitor',
`Enabled` tinyint(3) unsigned NOT NULL default '1',
@@ -383,15 +384,29 @@ CREATE TABLE `Monitors` (
--
-- Table structure for table `States`
+-- Added IsActive to track custom run states
--
DROP TABLE IF EXISTS `States`;
CREATE TABLE `States` (
+ `Id` int(10) unsigned NOT NULL auto_increment,
`Name` varchar(64) NOT NULL default '',
`Definition` text NOT NULL,
- PRIMARY KEY (`Name`)
+ `IsActive` tinyint(3) unsigned NOT NULL default '0',
+ PRIMARY KEY (`Id`)
) ENGINE=@ZM_MYSQL_ENGINE@;
+--
+-- Table structure for table `Servers`
+--
+
+DROP TABLE IF EXISTS `Servers`;
+CREATE TABLE `Servers` (
+ `Id` int(10) unsigned NOT NULL auto_increment,
+ `Name` varchar(64) NOT NULL default '',
+ `State_Id` int(10) unsigned,
+ PRIMARY KEY (`Id`)
+) ENGINE=@ZM_MYSQL_ENGINE@;
--
-- Table structure for table `Stats`
--
@@ -579,6 +594,11 @@ INSERT INTO Controls VALUES (NULL,'Toshiba IK-WB11A','Remote','Toshiba_IK_WB11A'
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);
+
+
+
--
-- Add some monitor preset values
diff --git a/db/zm_update-1.28.100.sql b/db/zm_update-1.28.100.sql
new file mode 100644
index 000000000..1b3d3b745
--- /dev/null
+++ b/db/zm_update-1.28.100.sql
@@ -0,0 +1,22 @@
+--
+-- This updates a 1.28.99 database to 1.28.100
+--
+
+--
+-- Add ServerId column to Monitors
+--
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*)
+ FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE table_name = 'Monitors'
+ AND table_schema = DATABASE()
+ AND column_name = 'ServerId'
+ ) > 0,
+"SELECT 'Column ServerId exists in Monitors'",
+"ALTER TABLE Monitors ADD `ServerId` int(10) unsigned AFTER `Name`"
+));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
+
diff --git a/db/zm_update-1.28.99.sql b/db/zm_update-1.28.99.sql
index 660b19db4..3ddd0c77d 100644
--- a/db/zm_update-1.28.99.sql
+++ b/db/zm_update-1.28.99.sql
@@ -4,6 +4,8 @@
--
-- Add Controls definition for ONVIF
+-- Add Controls definition for FI9831W
+-- Add Controls definition for FI8918W
--
INSERT INTO Controls
SELECT * FROM (SELECT NULL as Id,
@@ -108,9 +110,269 @@ SELECT * FROM (SELECT NULL as Id,
WHERE NOT EXISTS (
SELECT Name FROM Controls WHERE name = 'ONVIF Camera'
) LIMIT 1;
+
+INSERT INTO Controls
+SELECT * FROM (SELECT NULL as Id,
+ 'Foscam 9831W' as Name,
+ 'Ffmpeg' as Type,
+ 'FI9831W' as Protocol,
+ 0 as CanWake,
+ 0 as CanSleep,
+ 1 as CanReset,
+ 0 as CanZoom,
+ 0 as CanAutoZoom,
+ 0 as CanZoomAbs,
+ 0 as CanZoomRel,
+ 0 as CanZoomCon,
+ 0 as MinZoomRange,
+ 0 as MaxZoomRange,
+ 0 as MinZoomStep,
+ 0 as MaxZoomStep,
+ 0 as HasZoomSpeed,
+ 0 as MinZoomSpeed,
+ 0 as MaxZoomSpeed,
+ 0 as CanFocus,
+ 0 as CanAutoFocus,
+ 0 as CanFocusAbs,
+ 0 as CanFocusRel,
+ 0 as CanFocusCon,
+ 0 as MinFocusRange,
+ 0 as MaxFocusRange,
+ 0 as MinFocusStep,
+ 0 as MaxFocusStep,
+ 0 as HasFocusSpeed,
+ 0 as MinFocusSpeed,
+ 0 as MaxFocusSpeed,
+ 0 as CanIris,
+ 0 as CanAutoIris,
+ 0 as CanIrisAbs,
+ 0 as CanIrisRel,
+ 0 as CanIrisCon,
+ 0 as MinIrisRange,
+ 0 as MaxIrisRange,
+ 0 as MinIrisStep,
+ 0 as MaxIrisStep,
+ 0 as HasIrisSpeed,
+ 0 as MinIrisSpeed,
+ 0 as MaxIrisSpeed,
+ 0 as CanGain,
+ 0 as CanAutoGain,
+ 0 as CanGainAbs,
+ 0 as CanGainRel,
+ 0 as CanGainCon,
+ 0 as MinGainRange,
+ 0 as MaxGainRange,
+ 0 as MinGainStep,
+ 0 as MaxGainStep,
+ 0 as HasGainSpeed,
+ 0 as MinGainSpeed,
+ 0 as MaxGainSpeed,
+ 0 as CanWhite,
+ 0 as CanAutoWhite,
+ 0 as CanWhiteAbs,
+ 0 as CanWhiteRel,
+ 0 as CanWhiteCon,
+ 0 as MinWhiteRange,
+ 0 as MaxWhiteRange,
+ 0 as MinWhiteStep,
+ 0 as MaxWhiteStep,
+ 0 as HasWhiteSpeed,
+ 0 as MinWhiteSpeed,
+ 0 as MaxWhiteSpeed,
+ 0 as HasPresets,
+ 16 as NumPresets,
+ 1 as HasHomePreset,
+ 1 as CanSetPresets,
+ 1 as CanMove,
+ 1 as CanMoveDiag,
+ 0 as CanMoveMap,
+ 0 as CanMoveAbs,
+ 0 as CanMoveRel,
+ 1 as CanMoveCon,
+ 1 as CanPan,
+ 0 as MinPanRange,
+ 360 as MaxPanRange,
+ 0 as MinPanStep,
+ 360 as MaxPanStep,
+ 1 as HasPanSpeed,
+ 0 as MinPanSpeed,
+ 4 as MaxPanSpeed,
+ 0 as HasTurboPan,
+ 0 as TurboPanSpeed,
+ 1 as CanTilt,
+ 0 as MinTiltRange,
+ 90 as MaxTiltRange,
+ 0 as MinTiltStep,
+ 90 as MaxTiltStep,
+ 0 as HasTiltSpeed,
+ 0 as MinTiltSpeed,
+ 0 as MaxTiltSpeed,
+ 0 as HasTurboTilt,
+ 0 as TurboTiltSpeed,
+ 0 as CanAutoScan,
+ 0 as NumScanPaths) AS tmp
+WHERE NOT EXISTS (
+ SELECT Name FROM Controls WHERE name = 'Foscam 9831W'
+) LIMIT 1;
+
+INSERT INTO Controls
+SELECT * FROM (SELECT NULL as Id,
+ 'Foscam FI8918W' as Name,
+ 'Ffmpeg' as Type,
+ 'FI8918W' as Protocol,
+ 0 as CanWake,
+ 0 as CanSleep,
+ 1 as CanReset,
+ 0 as CanZoom,
+ 0 as CanAutoZoom,
+ 0 as CanZoomAbs,
+ 0 as CanZoomRel,
+ 0 as CanZoomCon,
+ 0 as MinZoomRange,
+ 0 as MaxZoomRange,
+ 0 as MinZoomStep,
+ 0 as MaxZoomStep,
+ 0 as HasZoomSpeed,
+ 0 as MinZoomSpeed,
+ 0 as MaxZoomSpeed,
+ 0 as CanFocus,
+ 0 as CanAutoFocus,
+ 0 as CanFocusAbs,
+ 0 as CanFocusRel,
+ 0 as CanFocusCon,
+ 0 as MinFocusRange,
+ 0 as MaxFocusRange,
+ 0 as MinFocusStep,
+ 0 as MaxFocusStep,
+ 0 as HasFocusSpeed,
+ 0 as MinFocusSpeed,
+ 0 as MaxFocusSpeed,
+ 0 as CanIris,
+ 0 as CanAutoIris,
+ 0 as CanIrisAbs,
+ 0 as CanIrisRel,
+ 0 as CanIrisCon,
+ 0 as MinIrisRange,
+ 0 as MaxIrisRange,
+ 0 as MinIrisStep,
+ 0 as MaxIrisStep,
+ 0 as HasIrisSpeed,
+ 0 as MinIrisSpeed,
+ 0 as MaxIrisSpeed,
+ 0 as CanGain,
+ 0 as CanAutoGain,
+ 0 as CanGainAbs,
+ 0 as CanGainRel,
+ 0 as CanGainCon,
+ 0 as MinGainRange,
+ 0 as MaxGainRange,
+ 0 as MinGainStep,
+ 0 as MaxGainStep,
+ 0 as HasGainSpeed,
+ 0 as MinGainSpeed,
+ 0 as MaxGainSpeed,
+ 0 as CanWhite,
+ 0 as CanAutoWhite,
+ 0 as CanWhiteAbs,
+ 0 as CanWhiteRel,
+ 0 as CanWhiteCon,
+ 0 as MinWhiteRange,
+ 0 as MaxWhiteRange,
+ 0 as MinWhiteStep,
+ 0 as MaxWhiteStep,
+ 0 as HasWhiteSpeed,
+ 0 as MinWhiteSpeed,
+ 0 as MaxWhiteSpeed,
+ 0 as HasPresets,
+ 8 as NumPresets,
+ 0 as HasHomePreset,
+ 1 as CanSetPresets,
+ 1 as CanMove,
+ 1 as CanMoveDiag,
+ 0 as CanMoveMap,
+ 0 as CanMoveAbs,
+ 0 as CanMoveRel,
+ 1 as CanMoveCon,
+ 1 as CanPan,
+ 0 as MinPanRange,
+ 360 as MaxPanRange,
+ 0 as MinPanStep,
+ 360 as MaxPanStep,
+ 1 as HasPanSpeed,
+ 0 as MinPanSpeed,
+ 4 as MaxPanSpeed,
+ 0 as HasTurboPan,
+ 0 as TurboPanSpeed,
+ 1 as CanTilt,
+ 0 as MinTiltRange,
+ 90 as MaxTiltRange,
+ 0 as MinTiltStep,
+ 90 as MaxTiltStep,
+ 0 as HasTiltSpeed,
+ 0 as MinTiltSpeed,
+ 0 as MaxTiltSpeed,
+ 0 as HasTurboTilt,
+ 0 as TurboTiltSpeed,
+ 0 as CanAutoScan,
+ 0 as NumScanPaths) AS tmp
+WHERE NOT EXISTS (
+ SELECT Name FROM Controls WHERE name = 'Foscam FI8918W'
+) LIMIT 1;
+
--
-- Hide USE_DEEP_STORAGE from user to prevent accidental event loss
--
UPDATE `zm`.`Config` SET `Category`='hidden' WHERE `Name`='ZM_USE_DEEP_STORAGE';
+--
+-- Add Id column to State
+--
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*)
+ FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE table_name = 'States'
+ AND table_schema = DATABASE()
+ AND column_name = 'Id'
+ ) > 0,
+"SELECT 'Column Id exists in States'",
+"ALTER TABLE States DROP PRIMARY KEY, ADD `Id` int(10) unsigned auto_increment NOT NULL PRIMARY KEY FIRST"
+));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
+
+-- The States table will be updated to have a new column called IsActive
+-- used to keep track of which custom state is active (if any)
+SET @s = (SELECT IF(
+ (SELECT COUNT(*)
+ FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE table_name = 'States'
+ AND table_schema = DATABASE()
+ AND column_name = 'IsActive'
+ ) > 0,
+"SELECT 'Column IsActive exists in States'",
+"ALTER TABLE `States` ADD `IsActive` tinyint(3) unsigned not null default 0 AFTER `Definition`"
+));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*)
+ FROM INFORMATION_SCHEMA.TABLES
+ WHERE table_name = 'Servers'
+ AND table_schema = DATABASE()
+ ) > 0,
+"SELECT 'Servers table exists'",
+"CREATE TABLE `Servers` (
+ `Id` int(10) unsigned NOT NULL auto_increment,
+ `Name` varchar(64) NOT NULL default '',
+ `State_Id` int(10) unsigned,
+ PRIMARY KEY (`Id`)
+)"
+));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
diff --git a/distros/debian/control b/distros/debian/control
index edf15560c..d9564d6f9 100644
--- a/distros/debian/control
+++ b/distros/debian/control
@@ -7,7 +7,7 @@ Standards-Version: 3.9.4
Package: zoneminder
Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, apache2, libapache2-mod-php5 | libapache2-mod-fcgid, php5, php5-mysql|php5-mysqlnd, libphp-serialization-perl, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, mariadb-client|mysql-client, libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl, libpcre3, ffmpeg | libav-tools, rsyslog | system-log-daemon, libmodule-load-perl, libsys-mmap-perl, libjson-any-perl, netpbm, libavdevice53 | libavdevice55, libjpeg8|libjpeg9|libjpeg62-turbo, zip, libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl, libvlccore5 | libvlccore7 | libvlccore8, libvlc5, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev, libpolkit-gobject-1-0, liburi-encode-perl
+Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, apache2, libapache2-mod-php5 | libapache2-mod-fcgid, php5, php5-mysql|php5-mysqlnd, libphp-serialization-perl, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, mariadb-client|mysql-client, libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl, libpcre3, ffmpeg | libav-tools, rsyslog | system-log-daemon, libmodule-load-perl, libsys-mmap-perl, libjson-any-perl, netpbm, libavdevice53 | libavdevice55, libjpeg8|libjpeg9|libjpeg62-turbo, zip, libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl, libvlccore5 | libvlccore7 | libvlccore8, libvlc5, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev, libpolkit-gobject-1-0, liburi-encode-perl, php5-gd
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/debian8/control b/distros/debian8/control
index a378742dc..05eaee17e 100644
--- a/distros/debian8/control
+++ b/distros/debian8/control
@@ -95,7 +95,7 @@ Section: web
Architecture: any
Depends: zoneminder-core (= ${binary:Version}), ${shlibs:Depends},
${misc:Depends}, debconf, apache2, libapache2-mod-php5 | libapache2-mod-fcgid,
- php5, php5-mysql | php5-mysqlnd
+ php5, php5-mysql | php5-mysqlnd, php5-gd
Description: Essential files for ZoneMinder's web user interface
ZoneMinder is a video camera security and surveillance solution.
.
diff --git a/distros/debian_cmake/control b/distros/debian_cmake/control
index e08f1ac2c..2c9fb6c88 100644
--- a/distros/debian_cmake/control
+++ b/distros/debian_cmake/control
@@ -7,7 +7,7 @@ Standards-Version: 3.9.4
Package: zoneminder
Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, apache2 | httpd, libapache2-mod-php5 | libapache2-mod-fcgid | php5-fpm, php5-mysqlnd | php5-mysql, libphp-serialization-perl, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, mariadb-client | mysql-client, libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl, libpcre3, libav-tools, rsyslog | system-log-daemon, libmodule-load-perl, libsys-mmap-perl, libjson-any-perl, netpbm, libavdevice53, libjpeg8 | libjpeg9 | libjpeg62-turbo, zip, libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl, libvlccore5 | libvlccore7, libvlc5, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev, libpolkit-gobject-1-0
+Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, apache2 | httpd, libapache2-mod-php5 | libapache2-mod-fcgid | php5-fpm, php5-mysqlnd | php5-mysql, libphp-serialization-perl, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, mariadb-client | mysql-client, libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl, libpcre3, libav-tools, rsyslog | system-log-daemon, libmodule-load-perl, libsys-mmap-perl, libjson-any-perl, netpbm, libavdevice53, libjpeg8 | libjpeg9 | libjpeg62-turbo, zip, libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl, libvlccore5 | libvlccore7, libvlc5, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev, libpolkit-gobject-1-0, php5-gd
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/fedora/zoneminder.f20.spec b/distros/fedora/zoneminder.f20.spec
index 59dfa6c1e..b06db673d 100644
--- a/distros/fedora/zoneminder.f20.spec
+++ b/distros/fedora/zoneminder.f20.spec
@@ -24,7 +24,7 @@ Source: ZoneMinder-%{version}.tar.gz
BuildRequires: cmake gnutls-devel systemd-units bzip2-devel
BuildRequires: community-mysql-devel pcre-devel libjpeg-turbo-devel
-BuildRequires: perl(Archive::Tar) perl(Archive::Zip)
+BuildRequires: perl(Archive::Tar) perl(Archive::Zip) perl-podlators
BuildRequires: perl(Date::Manip) perl(DBD::mysql)
BuildRequires: perl(ExtUtils::MakeMaker) perl(LWP::UserAgent)
BuildRequires: perl(MIME::Entity) perl(MIME::Lite)
@@ -38,7 +38,7 @@ BuildRequires: gcc gcc-c++ vlc-devel libcurl-devel libv4l-devel
BuildRequires: httpd polkit-devel
%{!?_without_ffmpeg:BuildRequires: ffmpeg}
-Requires: httpd php php-mysql cambozola polkit net-tools psmisc
+Requires: httpd php php-gd php-mysql cambozola polkit net-tools psmisc
Requires: libjpeg-turbo vlc-core libcurl
Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version))
Requires: perl(DBD::mysql) perl(Archive::Tar) perl(Archive::Zip)
diff --git a/distros/fedora/zoneminder.f21.spec b/distros/fedora/zoneminder.f21.spec
index 94986336e..c1875cf5a 100644
--- a/distros/fedora/zoneminder.f21.spec
+++ b/distros/fedora/zoneminder.f21.spec
@@ -24,7 +24,7 @@ Source: ZoneMinder-%{version}.tar.gz
BuildRequires: cmake gnutls-devel systemd-units bzip2-devel
BuildRequires: community-mysql-devel pcre-devel libjpeg-turbo-devel
-BuildRequires: perl(Archive::Tar) perl(Archive::Zip)
+BuildRequires: perl(Archive::Tar) perl(Archive::Zip) perl-podlators
BuildRequires: perl(Date::Manip) perl(DBD::mysql)
BuildRequires: perl(ExtUtils::MakeMaker) perl(LWP::UserAgent)
BuildRequires: perl(MIME::Entity) perl(MIME::Lite)
@@ -38,7 +38,7 @@ BuildRequires: gcc gcc-c++ vlc-devel libcurl-devel libv4l-devel
BuildRequires: httpd polkit-devel
%{!?_without_ffmpeg:BuildRequires: ffmpeg}
-Requires: httpd php php-mysql cambozola polkit net-tools psmisc
+Requires: httpd php php-gd php-mysql cambozola polkit net-tools psmisc
Requires: libjpeg-turbo vlc-core libcurl
Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version))
Requires: perl(DBD::mysql) perl(Archive::Tar) perl(Archive::Zip)
diff --git a/distros/redhat/CMakeLists.txt b/distros/redhat/CMakeLists.txt
index 129d666d4..fe69c6371 100644
--- a/distros/redhat/CMakeLists.txt
+++ b/distros/redhat/CMakeLists.txt
@@ -16,7 +16,7 @@ else(ZM_TARGET_DISTRO STREQUAL "el7")
endif(ZM_TARGET_DISTRO STREQUAL "el7")
# Download jscalendar & move files into position
-file(DOWNLOAD http://softlayer-dal.dl.sourceforge.net/project/jscalendar/jscalendar/1.0/jscalendar-1.0.zip ${CMAKE_CURRENT_SOURCE_DIR}/jscalendar-1.0.zip LOG jsc_log STATUS download_jsc)
+file(DOWNLOAD http://nbtelecom.dl.sourceforge.net/project/jscalendar/jscalendar/1.0/jscalendar-1.0.zip ${CMAKE_CURRENT_SOURCE_DIR}/jscalendar-1.0.zip LOG jsc_log STATUS download_jsc)
#message(STATUS "Log of jscalender script was: ${jsc_log}")
if(download_jsc EQUAL 0)
message(STATUS "Jscalander successfully downloaded. Installing...")
diff --git a/distros/redhat/zoneminder.el6.spec b/distros/redhat/zoneminder.el6.spec
index e50db96e7..3a65a80db 100644
--- a/distros/redhat/zoneminder.el6.spec
+++ b/distros/redhat/zoneminder.el6.spec
@@ -30,7 +30,7 @@ BuildRequires: libcurl-devel vlc-devel ffmpeg-devel polkit-devel
# cmake needs the following installed at build time due to the way it auto-detects certain parameters
BuildRequires: httpd ffmpeg
-Requires: httpd php php-mysql mysql-server libjpeg-turbo polkit net-tools psmisc
+Requires: httpd php php-gd php-mysql mysql-server libjpeg-turbo polkit net-tools psmisc
Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version))
Requires: perl(DBD::mysql) perl(Archive::Tar) perl(Archive::Zip)
Requires: perl(MIME::Entity) perl(MIME::Lite) perl(Net::SMTP) perl(Net::FTP)
diff --git a/distros/redhat/zoneminder.el7.spec b/distros/redhat/zoneminder.el7.spec
index 63d7fe920..1d53cbdd5 100644
--- a/distros/redhat/zoneminder.el7.spec
+++ b/distros/redhat/zoneminder.el7.spec
@@ -20,7 +20,7 @@ Source: ZoneMinder-%{version}.tar.gz
BuildRequires: cmake gnutls-devel systemd-units bzip2-devel
BuildRequires: mariadb-devel pcre-devel libjpeg-turbo-devel
-BuildRequires: perl(Archive::Tar) perl(Archive::Zip)
+BuildRequires: perl(Archive::Tar) perl(Archive::Zip) perl-podlators
BuildRequires: perl(Date::Manip) perl(DBD::mysql)
BuildRequires: perl(ExtUtils::MakeMaker) perl(LWP::UserAgent)
BuildRequires: perl(MIME::Entity) perl(MIME::Lite)
@@ -32,7 +32,7 @@ BuildRequires: ffmpeg ffmpeg-devel perl(X10::ActiveHome) perl(Astro::SunTime)
# cmake needs the following installed at build time due to the way it auto-detects certain parameters
BuildRequires: httpd polkit-devel
-Requires: httpd php php-mysql mariadb-server polkit net-tools psmisc
+Requires: httpd php php-gd php-mysql mariadb-server polkit net-tools psmisc
Requires: libjpeg-turbo vlc-core libcurl
Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version))
Requires: perl(DBD::mysql) perl(Archive::Tar) perl(Archive::Zip)
diff --git a/distros/ubuntu1204/changelog b/distros/ubuntu1204/changelog
index 2dfc8b63c..09571c21b 100644
--- a/distros/ubuntu1204/changelog
+++ b/distros/ubuntu1204/changelog
@@ -1,3 +1,11 @@
+zoneminder (1.28.1+1-trusty-SNAPSHOT2015071501) trusty; urgency=medium
+
+ * fix for issue #586 and #932
+ * fix for db not being upgraded
+ * version upgraded to .100
+
+ -- Isaac Connor Wed, 15 Jul 2015 11:56:28 -0400
+
zoneminder (1.28.1+1-trusty-SNAPSHOT2015030201) trusty; urgency=medium
* maybe fix for RTSP Basic Auth
diff --git a/distros/ubuntu1204/control b/distros/ubuntu1204/control
index b1be82b34..170504b5a 100644
--- a/distros/ubuntu1204/control
+++ b/distros/ubuntu1204/control
@@ -7,7 +7,7 @@ Standards-Version: 3.9.4
Package: zoneminder
Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, apache2, libapache2-mod-php5 | libapache2-mod-fcgid, php5, php5-mysql|php5-mysqlnd, libphp-serialization-perl, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, mariadb-client|mysql-client|mysql-client-5.6, libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl, libpcre3, ffmpeg | libav-tools, rsyslog | system-log-daemon, libmodule-load-perl, libsys-mmap-perl, libjson-any-perl, libavdevice53 | libavdevice55, libjpeg8|libjpeg9|libjpeg62-turbo, zip, libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl, libvlccore5 | libvlccore7 | libvlccore8, libvlc5, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev, libpolkit-gobject-1-0, liburi-encode-perl
+Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, apache2, libapache2-mod-php5 | libapache2-mod-fcgid, php5, php5-mysql|php5-mysqlnd, libphp-serialization-perl, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, mariadb-client|mysql-client|mysql-client-5.6, libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl, libpcre3, ffmpeg | libav-tools, rsyslog | system-log-daemon, libmodule-load-perl, libsys-mmap-perl, libjson-any-perl, libavdevice53 | libavdevice55, libjpeg8|libjpeg9|libjpeg62-turbo, zip, libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl, libvlccore5 | libvlccore7 | libvlccore8, libvlc5, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev, libpolkit-gobject-1-0, liburi-encode-perl, php5-gd
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/ubuntu1504/changelog b/distros/ubuntu1504/changelog
index 2dfc8b63c..9dea00498 100644
--- a/distros/ubuntu1504/changelog
+++ b/distros/ubuntu1504/changelog
@@ -1,3 +1,11 @@
+zoneminder (1.28.1+1-vivid-SNAPSHOT2015071501) vivid; urgency=medium
+
+ * fix for issue #586 and #932
+ * fix for db not being upgraded
+ * version upgraded to .100
+
+ -- Isaac Connor Wed, 15 Jul 2015 11:56:28 -0400
+
zoneminder (1.28.1+1-trusty-SNAPSHOT2015030201) trusty; urgency=medium
* maybe fix for RTSP Basic Auth
diff --git a/distros/ubuntu1504/control b/distros/ubuntu1504/control
index 5b63046c3..142ec783c 100644
--- a/distros/ubuntu1504/control
+++ b/distros/ubuntu1504/control
@@ -7,7 +7,7 @@ Standards-Version: 3.9.4
Package: zoneminder
Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, apache2, libapache2-mod-php5 | libapache2-mod-fcgid, php5, php5-mysql|php5-mysqlnd, libphp-serialization-perl, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, mariadb-client|mysql-client|mysql-client-5.6, libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl, libpcre3, ffmpeg, rsyslog | system-log-daemon, libmodule-load-perl, libsys-mmap-perl, libjson-any-perl, libavdevice-ffmpeg56, libjpeg8|libjpeg9|libjpeg62-turbo, zip, libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl, libvlccore5 | libvlccore7 | libvlccore8, libvlc5, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev, libpolkit-gobject-1-0, liburi-encode-perl
+Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, apache2, libapache2-mod-php5 | libapache2-mod-fcgid, php5, php5-mysql|php5-mysqlnd, libphp-serialization-perl, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, mariadb-client|mysql-client|mysql-client-5.6, libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl, libpcre3, ffmpeg, rsyslog | system-log-daemon, libmodule-load-perl, libsys-mmap-perl, libjson-any-perl, libavdevice-ffmpeg56, libjpeg8|libjpeg9|libjpeg62-turbo, zip, libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl, libvlccore5 | libvlccore7 | libvlccore8, libvlc5, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev, libpolkit-gobject-1-0, liburi-encode-perl, php5-gd
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/ubuntu1504_cmake_split_packages/README.Debian b/distros/ubuntu1504_cmake_split_packages/README.Debian
new file mode 100644
index 000000000..a49b6be72
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/README.Debian
@@ -0,0 +1,51 @@
+zoneminder for Debian
+---------------------
+
+There is one manual step to get the web interface working.
+You need to link /etc/zm/apache.conf to /etc/apache2/conf.d/zoneminder.conf,
+then reload the apache config (i.e. /etc/init.d/apache2 reload)
+
+Changing the location for images and events
+-------------------------------------------
+
+Zoneminder, in its upstream form, stores data in /usr/share/zoneminder/. This
+package modifies that by changing /usr/share/zoneminder/images and
+/usr/share/zoneminder/events to symlinks to directories under
+/var/cache/zoneminder.
+
+There are numerous places these could be put and ways to do it. But, at the
+moment, if you change this, an upgrade will fail with a warning about these
+locations having changed (the reason for this was that previously, an upgrade
+would silently revert the changes and cause event loss - refer
+bug #608793).
+
+If you do want to change the location, here are a couple of suggestions.
+
+These lines would mount /dev/sdX1 to /video_storage, and then 'link' /video_storage
+to the locations that ZoneMinder expects them to be at.
+
+ /dev/sdX1 /video_storage ext4 defaults 0 2
+ /video_storage/zoneminder/images /var/cache/zoneminder/images none bind 0 2
+ /video_storage/zoneminder/events /var/cache/zoneminder/events none bind 0 2
+
+ or if you have a separate partition for each:
+
+ /dev/sdX1 /var/cache/zoneminder/images ext4 defaults 0 2
+ /dev/sdX2 /var/cache/zoneminder/events ext4 defaults 0 2
+
+
+
+ -- Peter Howard , Sun, 16 Jan 2010 01:35:51 +1100
+
+Access to /dev/video*
+---------------------
+
+For cameras which require access to /dev/video*, zoneminder may need the
+www-data user added to the video group in order to see those cameras:
+
+ adduser www-data video
+
+Note that all web applications running on the zoneminder server will then have
+access to all video devices on the system.
+
+ -- Vagrant Cascadian Sun, 27 Mar 2011 13:06:56 -0700
diff --git a/distros/ubuntu1504_cmake_split_packages/apache.conf b/distros/ubuntu1504_cmake_split_packages/apache.conf
new file mode 100644
index 000000000..92a2b6414
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/apache.conf
@@ -0,0 +1,9 @@
+Alias /zm /usr/share/zoneminder
+
+
+ php_flag register_globals off
+ Options Indexes FollowSymLinks
+
+ DirectoryIndex index.php
+
+
diff --git a/distros/ubuntu1504_cmake_split_packages/changelog b/distros/ubuntu1504_cmake_split_packages/changelog
new file mode 100644
index 000000000..37a592b94
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/changelog
@@ -0,0 +1,95 @@
+zoneminder (1.28.1+1-trusty-SNAPSHOT2015061201) vivid; urgency=medium
+
+ * translation fixes
+ * Merge angular api
+ * now build using cmake
+ * use split up packaging
+
+ -- Isaac Connor Fri, 12 Jun 2015 11:22:36 -0400
+
+zoneminder (1.28.1+1-trusty-SNAPSHOT2015030201) trusty; urgency=medium
+
+ * maybe fix for RTSP Basic Auth
+ * Also remove dependency on netpbm
+
+ -- Isaac Connor Mon, 02 Mar 2015 11:25:59 -0500
+
+zoneminder (1.28.1+1-utopic-SNAPSHOT2015022301) utopic; urgency=medium
+
+ * Big merge of onvif support and some fixes.
+
+ -- Isaac Connor Mon, 23 Feb 2015 19:45:45 -0500
+
+zoneminder (1.28.0+1-trusty-SNAPSHOT2015021201) trusty; urgency=medium
+
+ * add mysql-client-5.6 as a dependency instaed of mysql-client.
+
+ -- Isaac Connor Fri, 13 Feb 2015 09:35:13 -0500
+
+zoneminder (1.28.0+1-trusty-SNAPSHOT2015011101) trusty; urgency=medium
+
+ * small changes
+
+ -- Isaac Connor Fri, 12 Dec 2014 16:38:36 -0500
+
+zoneminder (1.28.0+1-utopic-SNAPSHOT2014112001) utopic; urgency=medium
+
+ * Various fixes and developments since 1.28.0. Includes Digest-Auth for HTTP and better for RTSP
+
+ -- Isaac Connor Thu, 20 Nov 2014 10:57:57 -0500
+
+zoneminder (1.28.0-trusty) trusty; urgency=medium
+
+ * Release
+
+ -- Isaac Connor Fri, 17 Oct 2014 09:25:55 -0400
+
+zoneminder (1.27.99+1-trusty-SNAPSHOT2014101401) trusty; urgency=medium
+
+ * Several PR merges in big push for 1.28.0
+
+ -- Isaac Connor Tue, 14 Oct 2014 09:28:29 -0400
+
+zoneminder (1.27.99+1-trusty-SNAPSHOT2014092601) trusty; urgency=medium
+
+ * style updates and db fixes for database logging filters
+
+ -- Isaac Connor Fri, 26 Sep 2014 14:44:45 -0400
+
+zoneminder (1.27.99+1-trusty-SNAPSHOT2014090801) trusty; urgency=medium
+
+ * several segfault fixes for local cameras
+
+ -- Isaac Connor Mon, 08 Sep 2014 16:56:57 -0400
+
+zoneminder (1.27.99+1-trusty-SNAPSHOT2014090701) trusty; urgency=medium
+
+ * Fix segfaults for local cameras, also now includes the systemd support patch
+
+ -- Isaac Connor Sun, 07 Sep 2014 17:19:01 -0400
+
+zoneminder (1.27.99+1-trusty-SNAPSHOT2014082102) trusty; urgency=medium
+
+ * Fix UI inputs for v4l multibuffer and captures per frame
+
+ -- Isaac Connor Thu, 21 Aug 2014 12:03:31 -0400
+
+zoneminder (1.27.99+1-trusty-SNAPSHOT2014082101) trusty; urgency=medium
+
+ * fall back to Config table values for V4l MultiBUffer and Captures Per Frame
+ * add mention of monitor page settings for thse in the config table
+
+ -- Isaac Connor Thu, 21 Aug 2014 10:04:46 -0400
+
+zoneminder (1.27.99+1-precise-SNAPSHOT2014080601) precise; urgency=medium
+
+ * improve error messages
+ * Make zmupdate re-run the most recent patch so that people running the daily builds get their db updates
+
+ -- Isaac Connor Wed, 06 Aug 2014 20:20:20 -0400
+
+zoneminder (1.27.0+1-trusty-v4ltomonitor-1) trusty; urgency=high
+
+ * Snapshot release -
+
+ -- Isaac Connor Wed, 09 Jul 2014 21:35:29 -0400
diff --git a/distros/ubuntu1504_cmake_split_packages/compat b/distros/ubuntu1504_cmake_split_packages/compat
new file mode 100644
index 000000000..ec635144f
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/compat
@@ -0,0 +1 @@
+9
diff --git a/distros/ubuntu1504_cmake_split_packages/control b/distros/ubuntu1504_cmake_split_packages/control
new file mode 100644
index 000000000..0e16fd5ca
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/control
@@ -0,0 +1,133 @@
+Source: zoneminder
+Section: net
+Priority: optional
+Maintainer: Isaac Connor
+Build-Depends: debhelper (>= 9), po-debconf (>= 1.0), autoconf, automake, libphp-serialization-perl, libgnutls-dev, libmysqlclient-dev | libmariadbclient-dev, libdbd-mysql-perl, libdate-manip-perl, libwww-perl, libjpeg8-dev | libjpeg9-dev | libjpeg62-turbo-dev, libpcre3-dev, libavcodec-ffmpeg-dev, libavformat-ffmpeg-dev, libswscale-ffmpeg-dev, libavutil-ffmpeg-dev, libv4l-dev (>= 0.8.3), libbz2-dev, libtool, libsys-mmap-perl, libavdevice-ffmpeg-dev, libdevice-serialport-perl, libarchive-zip-perl, libmime-lite-perl, dh-autoreconf, libvlccore-dev, libvlc-dev, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev, libgcrypt11-dev | libgcrypt20-dev, libpolkit-gobject-1-dev, libdbi-perl, libnet-sftp-foreign-perl, libexpect-perl, libmime-tools-perl, libx264-dev, libmp4v2-dev, libpcre3-dev
+Standards-Version: 3.9.6
+
+Package: zoneminder
+Section: metapackages
+Architecture: all
+Depends: ${misc:Depends},
+ libzoneminder-perl (>= ${source:Version}),
+ zoneminder-database (>= ${source:Version}),
+ zoneminder-core (>= ${binary:Version}),
+ zoneminder-ui-base (>= ${source:Version}),
+ zoneminder-ui-classic (>= ${source:Version}),
+ zoneminder-ui-mobile (>= ${source:Version}),
+ zoneminder-ui-xml (>= ${source:Version})
+Description: Video camera security and surveillance solution (metapackage)
+ ZoneMinder is intended for use in single or multi-camera video security
+ applications, including commercial or home CCTV, theft prevention and child
+ or family member or home monitoring and other care scenarios. It
+ supports capture, analysis, recording, and monitoring of video data coming
+ from one or more video or network cameras attached to a Linux system.
+ ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom
+ cameras using a variety of protocols. It is suitable for use as a home
+ video security system and for commercial or professional video security
+ and surveillance. It can also be integrated into a home automation system
+ via X.10 or other protocols.
+
+Package: libzoneminder-perl
+Section: perl
+Architecture: all
+Depends: ${misc:Depends}, ${perl:Depends}, libdbi-perl,
+ libdevice-serialport-perl, libimage-info-perl, libjson-any-perl,
+ libsys-mmap-perl, liburi-encode-perl, libwww-perl
+Description: Perl libraries for ZoneMinder
+ ZoneMinder is a video camera security and surveillance solution.
+ .
+ This package provides the libraries for the perl scripts, it can be used to
+ write custom interfaces as well.
+
+Package: zoneminder-database
+Section: database
+Architecture: all
+Depends: ${misc:Depends}, debconf, dbconfig-common,
+ mysql-client | mariadb-client
+Recommends: mysql-server | mariadb-server
+Description: Database management package for ZoneMinder
+ ZoneMinder is a video camera security and surveillance solution.
+ .
+ This package provides the sql files and maintenance scripts to perform all the
+ database operations (installation, upgrade or removal) on a local or a remote
+ server.
+
+Package: zoneminder-core
+Section: video
+Architecture: any
+Depends: libzoneminder-perl (= ${source:Version}),
+ zoneminder-database (= ${source:Version}), ${shlibs:Depends}, ${misc:Depends},
+ ${perl:Depends}, libarchive-tar-perl, libarchive-zip-perl, libdate-manip-perl,
+ libdbi-perl, libmodule-load-conditional-perl, libmime-lite-perl,
+ libmime-tools-perl, libnet-sftp-foreign-perl, libphp-serialization-perl,
+ debconf, ffmpeg | libav-tools, rsyslog | system-log-daemon, zip,
+ policykit-1, apache2, libmp4v2-2, libpcre++0
+Description: Core binaries and perl scripts for ZoneMinder
+ ZoneMinder is a video camera security and surveillance solution.
+ .
+ This package provides the executable compiled binaries which do the main video
+ processing work and the perl scripts which perform helper and/or external
+ interface tasks.
+
+Package: zoneminder-core-dbg
+Priority: extra
+Section: debug
+Architecture: any
+Depends: zoneminder-core (= ${binary:Version}), ${misc:Depends}
+Description: Debugging symbols for ZoneMinder
+ ZoneMinder is a video camera security and surveillance solution.
+ .
+ This package provides the debugging symbols for the executable compiled
+ binaries.
+
+Package: zoneminder-ui-base
+Section: web
+Architecture: any
+Depends: zoneminder-core (= ${binary:Version}), ${shlibs:Depends},
+ ${misc:Depends}, debconf, apache2, libapache2-mod-php5 | libapache2-mod-fcgid,
+ php5, php5-mysql | php5-mysqlnd, php5-gd
+Description: Essential files for ZoneMinder's web user interface
+ ZoneMinder is a video camera security and surveillance solution.
+ .
+ This package provides the essential web files and maintenance scripts to set up
+ a basic web environment.
+
+Package: zoneminder-ui-classic
+Section: web
+Architecture: all
+Depends: zoneminder-ui-base (>= ${source:Version}), ${misc:Depends}
+Description: Classic web user interface for ZoneMinder
+ ZoneMinder is a video camera security and surveillance solution.
+ .
+ This package provides the classic web user interface.
+
+Package: zoneminder-ui-mobile
+Section: web
+Architecture: all
+Depends: zoneminder-ui-base (>= ${source:Version}), ${misc:Depends}
+Description: Mobile web user interface for ZoneMinder
+ ZoneMinder is a video camera security and surveillance solution.
+ .
+ This package provides the web user interface for mobile devices.
+
+Package: zoneminder-ui-xml
+Section: web
+Architecture: all
+Depends: zoneminder-ui-base (>= ${source:Version}), ${misc:Depends}
+Description: XML interface for ZoneMinder
+ ZoneMinder is a video camera security and surveillance solution.
+ .
+ This package provides a XML interface mainly intended for use with the eyeZm
+ iPhone Application, but can be used with any other custom programs as well.
+
+Package: zoneminder-ui-api
+Section: web
+Architecture: all
+Depends: zoneminder-ui-base (>= ${source:Version}), ${misc:Depends}
+Description: API interface for ZoneMinder
+ ZoneMinder is a video camera security and surveillance solution.
+ .
+ This package provides an API interface mainly intended for use with angular-ui
+ or mobile applications, but can be used with any other custom programs as well.
+
diff --git a/distros/ubuntu1504_cmake_split_packages/copyright b/distros/ubuntu1504_cmake_split_packages/copyright
new file mode 100644
index 000000000..a177502a0
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/copyright
@@ -0,0 +1,22 @@
+Copyright:
+
+Copyright 2002 Philip Coombes
+
+License:
+
+This package is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+This package is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public
+License along with this package; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+On Debian GNU/Linux systems, the text of the GPL can be found in
+/usr/share/common-licenses/GPL.
diff --git a/distros/ubuntu1504_cmake_split_packages/docs b/distros/ubuntu1504_cmake_split_packages/docs
new file mode 100644
index 000000000..b43bf86b5
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/docs
@@ -0,0 +1 @@
+README.md
diff --git a/distros/ubuntu1504_cmake_split_packages/libzoneminder-perl.install b/distros/ubuntu1504_cmake_split_packages/libzoneminder-perl.install
new file mode 100644
index 000000000..792ffc15e
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/libzoneminder-perl.install
@@ -0,0 +1,4 @@
+usr/share/perl5/ZoneMinder
+usr/share/perl5/ZoneMinder.pm
+debian/tmp/usr/share/man/man3/ZoneMinder.3pm
+debian/tmp/usr/share/man/man3/ZoneMinder::*
diff --git a/distros/ubuntu1504_cmake_split_packages/patches/series b/distros/ubuntu1504_cmake_split_packages/patches/series
new file mode 100644
index 000000000..e69de29bb
diff --git a/distros/ubuntu1504_cmake_split_packages/po/POTFILES.in b/distros/ubuntu1504_cmake_split_packages/po/POTFILES.in
new file mode 100644
index 000000000..5b155907e
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/po/POTFILES.in
@@ -0,0 +1,3 @@
+[type: gettext/rfc822deb] zoneminder-core.templates
+[type: gettext/rfc822deb] zoneminder-database.templates
+[type: gettext/rfc822deb] zoneminder-ui-base.templates
diff --git a/distros/ubuntu1504_cmake_split_packages/po/fr.po b/distros/ubuntu1504_cmake_split_packages/po/fr.po
new file mode 100644
index 000000000..85ced7fd2
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/po/fr.po
@@ -0,0 +1,252 @@
+# debconf french translation file for ZoneMinder.
+# Copyright (C) 2001-2008 Philip Coombes
+# This file is distributed under the same license as the zoneminder package.
+# First author: Emmanuel Papin , 2014.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: zoneminder\n"
+"Report-Msgid-Bugs-To: zoneminder@packages.debian.org\n"
+"POT-Creation-Date: 2014-12-16 12:34+0100\n"
+"PO-Revision-Date: 2014-12-07 00:40+0100\n"
+"Last-Translator: Emmanuel Papin \n"
+"Language-Team: French \n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Type: boolean
+#. Description
+#: ../zoneminder-core.templates:1001
+msgid "Delete this non empty directory?"
+msgstr "Supprimer ce répertoire non vide ?"
+
+#. Type: boolean
+#. Description
+#: ../zoneminder-core.templates:1001
+msgid ""
+"A purge of the ZoneMinder package is performed but the directory '/var/cache/"
+"zoneminder' is not empty so it will not be deleted."
+msgstr ""
+"Une purge du paquet ZoneMinder est en cours mais le répertoire '/var/cache/"
+"zoneminder' n'est pas vide et sera donc conservé."
+
+#. Type: boolean
+#. Description
+#: ../zoneminder-core.templates:1001
+msgid ""
+"Please consider that this directory is designed to contain data resulting "
+"from event detection. Therefore, \"proof of evidence\" could be lost!\""
+msgstr ""
+"Veuillez considérer que ce répertoire est conçu pour contenir des données "
+"résultants de la détection d'événements. Par conséquent, des preuves "
+"pourraient être perdues !"
+
+#. Type: boolean
+#. Description
+#: ../zoneminder-core.templates:1001
+msgid ""
+"If you are not sure of your decision, please do not delete this directory "
+"but perform a manual checkup."
+msgstr ""
+"Si vous n'êtes pas sûr de votre décision, veuillez conserver ce répertoire "
+"et effectuer une vérification manuelle."
+
+#. Type: boolean
+#. Description
+#: ../zoneminder-core.templates:2001
+msgid "Deletion confirmed?"
+msgstr "Supression confirmée ?"
+
+#. Type: boolean
+#. Description
+#: ../zoneminder-core.templates:2001
+msgid ""
+"You have allowed the deletion of directory '/var/cache/zoneminder' although "
+"it may contain critical data."
+msgstr ""
+"Vous avez autorisé la suppression du répertoire '/var/cache/zoneminder' bien "
+"qu'il puisse contenir des données critiques."
+
+#. Type: select
+#. Choices
+#: ../zoneminder-database.templates:1001
+msgid "local"
+msgstr "local"
+
+#. Type: select
+#. Choices
+#: ../zoneminder-database.templates:1001
+msgid "remote"
+msgstr "distant"
+
+#. Type: select
+#. Description
+#: ../zoneminder-database.templates:1002
+msgid "Database location:"
+msgstr "Emplacement de la base de donnée :"
+
+#. Type: select
+#. Description
+#: ../zoneminder-database.templates:1002
+msgid ""
+"A database server is required to run ZoneMinder. The database can be "
+"installed either locally or remotely on a machine of your network."
+msgstr ""
+"Un serveur de base de données est requis pour ZoneMinder. La base de donnée "
+"peut être installée localement ou à distance sur une machine de votre réseau."
+
+#. Type: select
+#. Description
+#: ../zoneminder-database.templates:1002
+msgid ""
+"If you choose a remote location, you will have to select the 'tcp/ip' "
+"connection method and enter the hostname or ip address of the remote machine "
+"in the next configuration screens."
+msgstr ""
+"Si vous choisissez un emplacement distant, vous devrez sélectionner la "
+"méthode de connexion 'tcp/ip' et entrer le nom réseau ou l'adresse ip de la "
+"machine distante dans les écrans de configuration suivants."
+
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:2001
+msgid "No local database server is available:"
+msgstr "Aucun serveur local de base de données n'est disponible :"
+
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:2001
+msgid ""
+"Currently ZoneMinder supports mysql or mariadb database server but none of "
+"them appears to be installed on this machine."
+msgstr ""
+"Actuellement ZoneMinder supporte les serveurs de base de données mysql et "
+"mariadb mais aucun d'entre eux n'est installé sur cette machine."
+
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:2001
+msgid ""
+"In order to complete ZoneMinder's installation, after ending of this "
+"assistant, please install a compatible database server and then restart the "
+"assistant by invoking:"
+msgstr ""
+"Afin de compléter l'installation de ZoneMinder, après la fermeture de cet "
+"assitant, veuillez installer un serveur de base de données compatible et "
+"ensuite redémarrez l'assistant en invoquant :"
+
+#. Type: error
+#. Description
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:2001 ../zoneminder-database.templates:3001
+msgid "$ sudo dpkg-reconfigure zoneminder"
+msgstr "$ sudo dpkg-reconfigure zoneminder"
+
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:3001
+msgid "Remote database servers are not allowed:"
+msgstr "Les serveurs de base de données distants ne sont pas autorisés :"
+
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:3001
+msgid ""
+"The current configuration of dbconfig-common does not allow installation of "
+"a database on remote servers."
+msgstr ""
+"La configuration actuelle de dbconfig-common ne permet pas l'installation de "
+"bases de données sur des serveurs distants."
+
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:3001
+msgid ""
+"In order to reconfigure dbconfig-common, please invoke the following command "
+"after ending of this assistant:"
+msgstr ""
+"Afin de reconfigurer dbconfig-common, veuillez invoquer la commande suivante "
+"après la fermeture de cet assitant :"
+
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:3001
+msgid "$ sudo dpkg-reconfigure dbconfig-common"
+msgstr "$ sudo dpkg-reconfigure dbconfig-common"
+
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:3001
+msgid ""
+"Then, to complete ZoneMinder's installation, please restart this assistant "
+"by invoking:"
+msgstr ""
+"Ensuite, pour compléter l'installation de ZoneMinder, veuillez redémarrer "
+"cet assistant en invoquant :"
+
+#. Type: password
+#. Description
+#: ../zoneminder-database.templates:4001
+msgid "New password for the ZoneMinder 'admin' user:"
+msgstr "Nouveau mot de passe pour le compte 'admin' de ZoneMinder :"
+
+#. Type: password
+#. Description
+#: ../zoneminder-database.templates:4001
+msgid "Please enter the password of the default administrative user."
+msgstr "Veuillez entrer le mot de passe du compte administrateur par défaut."
+
+#. Type: password
+#. Description
+#: ../zoneminder-database.templates:4001
+msgid ""
+"While not mandatory, it is highly recommended that you set a custom password "
+"for the administrative 'admin' user."
+msgstr ""
+"Bien que cela ne soit pas obligatoire, il est fortement recommandé de "
+"fournir un mot de passe personnalisé pour le compte administrateur 'admin'."
+
+#. Type: password
+#. Description
+#: ../zoneminder-database.templates:4001
+msgid "If this field is left blank, the password will not be changed."
+msgstr "Si le champ est laissé vide, le mot de passe ne sera pas changé."
+
+#. Type: password
+#. Description
+#: ../zoneminder-database.templates:5001
+msgid "Repeat password for the ZoneMinder 'admin' user:"
+msgstr "Répéter le mot de passe pour le compte 'admin' de ZoneMinder :"
+
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:6001
+msgid "Password input error"
+msgstr "Erreur de mot de passe"
+
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:6001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr ""
+"Les deux mots de passe saisis ne sont pas les mêmes. Veuillez essayer à "
+"nouveau."
+
+#. Type: multiselect
+#. Description
+#: ../zoneminder-ui-base.templates:1001
+msgid "Web server to reconfigure automatically:"
+msgstr "Serveur web à reconfigurer automatiquement :"
+
+#. Type: multiselect
+#. Description
+#: ../zoneminder-ui-base.templates:1001
+msgid ""
+"Please choose the web server that should be automatically configured for "
+"ZoneMinder's web portal access."
+msgstr ""
+"Veuillez choisir le serveur web à reconfigurer automatiquement pour l'accès "
+"au portail web de ZoneMinder."
diff --git a/distros/ubuntu1504_cmake_split_packages/po/templates.pot b/distros/ubuntu1504_cmake_split_packages/po/templates.pot
new file mode 100644
index 000000000..941a4094e
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/po/templates.pot
@@ -0,0 +1,222 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: zoneminder\n"
+"Report-Msgid-Bugs-To: zoneminder@packages.debian.org\n"
+"POT-Creation-Date: 2014-12-16 12:34+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Type: boolean
+#. Description
+#: ../zoneminder-core.templates:1001
+msgid "Delete this non empty directory?"
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../zoneminder-core.templates:1001
+msgid ""
+"A purge of the ZoneMinder package is performed but the directory '/var/cache/"
+"zoneminder' is not empty so it will not be deleted."
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../zoneminder-core.templates:1001
+msgid ""
+"Please consider that this directory is designed to contain data resulting "
+"from event detection. Therefore, \"proof of evidence\" could be lost!\""
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../zoneminder-core.templates:1001
+msgid ""
+"If you are not sure of your decision, please do not delete this directory "
+"but perform a manual checkup."
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../zoneminder-core.templates:2001
+msgid "Deletion confirmed?"
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../zoneminder-core.templates:2001
+msgid ""
+"You have allowed the deletion of directory '/var/cache/zoneminder' although "
+"it may contain critical data."
+msgstr ""
+
+#. Type: select
+#. Choices
+#: ../zoneminder-database.templates:1001
+msgid "local"
+msgstr ""
+
+#. Type: select
+#. Choices
+#: ../zoneminder-database.templates:1001
+msgid "remote"
+msgstr ""
+
+#. Type: select
+#. Description
+#: ../zoneminder-database.templates:1002
+msgid "Database location:"
+msgstr ""
+
+#. Type: select
+#. Description
+#: ../zoneminder-database.templates:1002
+msgid ""
+"A database server is required to run ZoneMinder. The database can be "
+"installed either locally or remotely on a machine of your network."
+msgstr ""
+
+#. Type: select
+#. Description
+#: ../zoneminder-database.templates:1002
+msgid ""
+"If you choose a remote location, you will have to select the 'tcp/ip' "
+"connection method and enter the hostname or ip address of the remote machine "
+"in the next configuration screens."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:2001
+msgid "No local database server is available:"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:2001
+msgid ""
+"Currently ZoneMinder supports mysql or mariadb database server but none of "
+"them appears to be installed on this machine."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:2001
+msgid ""
+"In order to complete ZoneMinder's installation, after ending of this "
+"assistant, please install a compatible database server and then restart the "
+"assistant by invoking:"
+msgstr ""
+
+#. Type: error
+#. Description
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:2001 ../zoneminder-database.templates:3001
+msgid "$ sudo dpkg-reconfigure zoneminder"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:3001
+msgid "Remote database servers are not allowed:"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:3001
+msgid ""
+"The current configuration of dbconfig-common does not allow installation of "
+"a database on remote servers."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:3001
+msgid ""
+"In order to reconfigure dbconfig-common, please invoke the following command "
+"after ending of this assistant:"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:3001
+msgid "$ sudo dpkg-reconfigure dbconfig-common"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:3001
+msgid ""
+"Then, to complete ZoneMinder's installation, please restart this assistant "
+"by invoking:"
+msgstr ""
+
+#. Type: password
+#. Description
+#: ../zoneminder-database.templates:4001
+msgid "New password for the ZoneMinder 'admin' user:"
+msgstr ""
+
+#. Type: password
+#. Description
+#: ../zoneminder-database.templates:4001
+msgid "Please enter the password of the default administrative user."
+msgstr ""
+
+#. Type: password
+#. Description
+#: ../zoneminder-database.templates:4001
+msgid ""
+"While not mandatory, it is highly recommended that you set a custom password "
+"for the administrative 'admin' user."
+msgstr ""
+
+#. Type: password
+#. Description
+#: ../zoneminder-database.templates:4001
+msgid "If this field is left blank, the password will not be changed."
+msgstr ""
+
+#. Type: password
+#. Description
+#: ../zoneminder-database.templates:5001
+msgid "Repeat password for the ZoneMinder 'admin' user:"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:6001
+msgid "Password input error"
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../zoneminder-database.templates:6001
+msgid "The two passwords you entered were not the same. Please try again."
+msgstr ""
+
+#. Type: multiselect
+#. Description
+#: ../zoneminder-ui-base.templates:1001
+msgid "Web server to reconfigure automatically:"
+msgstr ""
+
+#. Type: multiselect
+#. Description
+#: ../zoneminder-ui-base.templates:1001
+msgid ""
+"Please choose the web server that should be automatically configured for "
+"ZoneMinder's web portal access."
+msgstr ""
diff --git a/distros/ubuntu1504_cmake_split_packages/rules b/distros/ubuntu1504_cmake_split_packages/rules
new file mode 100755
index 000000000..a534e8089
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/rules
@@ -0,0 +1,164 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+# These are used for cross-compiling and for saving the configure script
+# from having to guess our platform (since we know it already)
+DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+
+CFLAGS = -Wall
+CPPFLAGS = -D__STDC_CONSTANT_MACROS
+CXXFLAGS = -DHAVE_LIBCRYPTO
+
+ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS)))
+DEBOPT = --enable-debug
+CFLAGS += -g
+CXXFLAGS += -g
+else
+DEBOPT =
+endif
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+CFLAGS += -O0
+else
+CFLAGS += -O2
+endif
+
+INSTDIR = debian/tmp
+
+# These are used to get the most recent version of the original sources from github
+UURL = $(shell git config --get remote.origin.url)
+BRANCH = $(shell git rev-parse --abbrev-ref HEAD)
+HEAD = $(shell git rev-parse HEAD)
+PKD = $(abspath $(dir $(MAKEFILE_LIST)))
+PKG = $(word 2,$(shell dpkg-parsechangelog -l$(PKD)/changelog | grep ^Source))
+VER ?= $(shell dpkg-parsechangelog -l$(PKD)/changelog | perl -ne 'print $$1 if m{^Version:\s+(?:\d+:)?(\d.*)(?:\-|\+nmu\d+.*)};')
+DTYPE =
+TARBALL = ../$(PKG)_$(VER)$(DTYPE).orig.tar.xz
+
+%:
+ dh $@ --buildsystem=cmake --parallel
+
+override_dh_auto_configure:
+ CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" dh_auto_configure -- \
+ -DCMAKE_INSTALL_PREFIX=/usr \
+ -DCMAKE_SKIP_RPATH=ON \
+ -DCMAKE_VERBOSE_MAKEFILE=OFF \
+ -DCMAKE_COLOR_MAKEFILE=ON \
+ -DZM_RUNDIR=/var/run/zm \
+ -DZM_SOCKDIR=/var/run/zm \
+ -DZM_TMPDIR=/var/tmp/zm \
+ -DZM_LOGDIR=/var/log/zm \
+ -DZM_WEBDIR=/usr/share/zoneminder \
+ -DZM_CONTENTDIR=/var/cache/zoneminder \
+ -DZM_CGIDIR=/usr/lib/cgi-bin \
+ -DZM_WEB_USER=www-data \
+ -DZM_WEB_GROUP=www-data \
+ -DCMAKE_INSTALL_SYSCONFDIR=etc/zm
+
+override_dh_auto_test:
+ # do not run tests...
+
+
+override_dh_clean:
+ # Add here commands to clean up after the build process.
+ [ ! -f Makefile ] || $(MAKE) distclean
+ dh_clean src/zm_config_defines.h
+ #
+ # Delete remaining auto-generated Makefile if Makefile.in exists
+ find $(CURDIR)/ -type f -name "Makefile" | while read file; do \
+ [ -f $$file.in ] && rm -f $$file; \
+ done || true
+ #
+ # Delete remaining auto-generated Makefile.in if Makefile.am exists
+ find $(CURDIR)/ -type f -name "Makefile.in" | while read filein; do \
+ fileam=`echo $$filein | sed 's/\(.*\)\.in/\1\.am/'`; \
+ [ -f $$fileam ] && rm -f $$filein; \
+ done || true
+
+override_dh_install:
+ dh_install --fail-missing
+ #
+ # NOTE: This is a short-term kludge; hopefully changes in the next
+ # upstream version will render this unnecessary.
+ rm -rf debian/zoneminder/usr/share/zoneminder/events
+ rm -rf debian/zoneminder/usr/share/zoneminder/images
+ rm -rf debian/zoneminder/usr/share/zoneminder/temp
+ # The link stuff for these folders has been moved to
+ # zoneminder-core.links file
+ #
+ # This is a slightly lesser kludge; moving the cgi stuff to
+ # /usr/share/zoneminder/cgi-bin breaks one set of behavior,
+ # having it just in /usr/lib/cgi-bin breaks another bit of
+ # behavior.
+ # The link stuff for /usr/share/zoneminder/cgi-bin has been moved to
+ # zoneminder-ui-base.links file
+
+override_dh_installinit:
+ dh_installinit --package=zoneminder-core --name=zoneminder
+
+override_dh_systemd_start:
+ dh_systemd_start --package=zoneminder-core --name=zoneminder \
+ --restart-after-upgrade
+
+override_dh_systemd_enable:
+ dh_systemd_enable --package=zoneminder-core --name=zoneminder
+
+override_dh_fixperms:
+ dh_fixperms
+ #
+ # As requested by the Debian Webapps Policy Manual §3.2.1
+ chown root:www-data debian/zoneminder-core/etc/zm/zm.conf
+ chmod 640 debian/zoneminder-core/etc/zm/zm.conf
+
+.PHONY: override_dh_strip
+override_dh_strip:
+ dh_strip --dbg-package=zoneminder-core-dbg
+
+# Inspired by https://wiki.debian.org/onlyjob/get-orig-source
+.PHONY: get-orig-source
+get-orig-source: $(TARBALL) $(info I: $(PKG)_$(VER)$(DTYPE))
+ @
+
+$(TARBALL):
+ $(if $(wildcard $(PKG)-$(VER)),$(error folder '$(PKG)-$(VER)' exists, aborting...))
+ @echo "# Cloning origin repository..."; \
+ if ! git clone $(UURL) $(PKG)-$(VER); then \
+ $(RM) -r $(PKG)-$(VER); \
+ echo "failed to clone repository, aborting..."; \
+ false; \
+ fi
+ @if [ $(BRANCH) != "master" ]; then \
+ cd $(PKG)-$(VER); \
+ echo "# Not on master branch, fetching origin branch '$(BRANCH)'..."; \
+ git fetch origin $(BRANCH):$(BRANCH) || false; \
+ echo "# Switching to branch '$(BRANCH)'..."; \
+ git checkout $(BRANCH) || false; \
+ fi
+ @echo "# Checking local source..."
+ @if [ $$(cd $(PKG)-$(VER) && git rev-parse HEAD) = $(HEAD) ]; then \
+ echo "even with origin, ok"; \
+ true; \
+ else \
+ echo "not even with origin, aborting..."; \
+ false; \
+ fi
+ @echo "# Setting times..."
+ @cd $(PKG)-$(VER) \
+ && for F in $$(git ls-tree -r --name-only HEAD | sed -e "s/\s/\*/g"); do \
+ touch --no-dereference -d "$$(git log -1 --format="%ai" -- $$F)" "$$F"; \
+ done
+ @echo "# Cleaning-up..."
+ cd $(PKG)-$(VER) && $(RM) -r .git
+ @echo "# Packing file '$(TARBALL)'..."
+ @find -L "$(PKG)-$(VER)" -xdev -type f -print | sort \
+ | XZ_OPT="-6v" tar -caf "$(TARBALL)" -T- --owner=root --group=root --mode=a+rX \
+ && $(RM) -r "$(PKG)-$(VER)"
diff --git a/distros/ubuntu1504_cmake_split_packages/source/format b/distros/ubuntu1504_cmake_split_packages/source/format
new file mode 100644
index 000000000..89ae9db8f
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/source/format
@@ -0,0 +1 @@
+3.0 (native)
diff --git a/distros/ubuntu1504_cmake_split_packages/source/local-options b/distros/ubuntu1504_cmake_split_packages/source/local-options
new file mode 100644
index 000000000..e69de29bb
diff --git a/distros/ubuntu1504_cmake_split_packages/source/options b/distros/ubuntu1504_cmake_split_packages/source/options
new file mode 100644
index 000000000..8bd61fce6
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/source/options
@@ -0,0 +1 @@
+extend-diff-ignore = "(^|/)(config\.sub|config\.guess|Makefile|aclocal.m4|compile|config.h.in|configure|depcomp|install-sh|missing)$"
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-core.config b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.config
new file mode 100644
index 000000000..2a15a599e
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.config
@@ -0,0 +1,11 @@
+#!/bin/sh
+# config maintainer script for zoneminder-core package
+
+set -e
+
+# Source the debconf stuff
+. /usr/share/debconf/confmodule
+
+#DEBHELPER#
+
+exit 0
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-core.dirs b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.dirs
new file mode 100644
index 000000000..350c32aff
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.dirs
@@ -0,0 +1,4 @@
+var/log/zm
+var/cache/zoneminder/events
+var/cache/zoneminder/images
+var/cache/zoneminder/temp
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-core.install b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.install
new file mode 100644
index 000000000..bd0a03bc2
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.install
@@ -0,0 +1,5 @@
+etc/zm
+usr/bin
+usr/share/polkit-1/actions
+usr/share/polkit-1/rules.d
+usr/share/man/man8
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-core.links b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.links
new file mode 100644
index 000000000..5560a100a
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.links
@@ -0,0 +1,3 @@
+var/cache/zoneminder/events usr/share/zoneminder/events
+var/cache/zoneminder/images usr/share/zoneminder/images
+var/cache/zoneminder/temp usr/share/zoneminder/temp
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-core.postinst b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.postinst
new file mode 100644
index 000000000..da2b444fe
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.postinst
@@ -0,0 +1,80 @@
+#! /bin/sh
+# postinst maintainer script for zoneminder-core package
+
+set -e
+
+# Source the debconf stuff
+. /usr/share/debconf/confmodule
+
+# Source the config file
+CONFIGFILE=/etc/zm/zm.conf
+. $CONFIGFILE
+
+# Do this when the package is installed, upgraded or reconfigured
+if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then
+
+ # Retrieve data from dbconfig (inputs from user)
+ . /etc/dbconfig-common/zoneminder.conf
+
+ # ZoneMinder config file handling
+ # Inspired by: http://manpages.debian.org/cgi-bin/man.cgi?query=debconf-devel&sektion=7
+
+ # Backup the config file
+ cp -a -f $CONFIGFILE ${CONFIGFILE}.postinst.bak
+
+ # Redeclare variables if missing in config file
+ test -z $dbc_dbserver || grep -Eq "^ *ZM_DB_HOST=" $CONFIGFILE \
+ || echo "ZM_DB_HOST=" >> ${CONFIGFILE}.postinst.bak
+ test -z $dbc_dbname || grep -Eq "^ *ZM_DB_NAME=" $CONFIGFILE \
+ || echo "ZM_DB_NAME=" >> ${CONFIGFILE}.postinst.bak
+ test -z $dbc_dbuser || grep -Eq "^ *ZM_DB_USER=" $CONFIGFILE \
+ || echo "ZM_DB_USER=" >> ${CONFIGFILE}.postinst.bak
+ test -z $dbc_dbpass || grep -Eq "^ *ZM_DB_PASS=" $CONFIGFILE \
+ || echo "ZM_DB_PASS=" >> ${CONFIGFILE}.postinst.bak
+
+ # Prevent ZM_DB_HOST to be empty if user selected the 'unix socket' method
+ if test -z $dbc_dbserver; then
+ dbc_dbserver_override="localhost"
+ else
+ dbc_dbserver_override=$dbc_dbserver
+ fi
+
+ # Update variables in config file
+ sed -i "s/^ *ZM_DB_HOST=.*/ZM_DB_HOST=$dbc_dbserver_override/" \
+ ${CONFIGFILE}.postinst.bak
+ test -z $dbc_dbname || sed -i "s/^ *ZM_DB_NAME=.*/ZM_DB_NAME=$dbc_dbname/" \
+ ${CONFIGFILE}.postinst.bak
+ test -z $dbc_dbuser || sed -i "s/^ *ZM_DB_USER=.*/ZM_DB_USER=$dbc_dbuser/" \
+ ${CONFIGFILE}.postinst.bak
+ test -z $dbc_dbpass || sed -i "s/^ *ZM_DB_PASS=.*/ZM_DB_PASS=$dbc_dbpass/" \
+ ${CONFIGFILE}.postinst.bak
+
+ # Clean-up backup file
+ mv -f ${CONFIGFILE}.postinst.bak $CONFIGFILE
+
+
+ # Set some file permissions
+ chown $ZM_WEB_USER:$ZM_WEB_GROUP /var/log/zm
+ if [ -z "$2" ]; then
+ chown $ZM_WEB_USER:$ZM_WEB_GROUP -R /var/cache/zoneminder
+ fi
+ # As requested by the Debian Webapps Policy Manual §3.2.1
+ chown root:${ZM_WEB_GROUP} $CONFIGFILE
+ chmod 640 $CONFIGFILE
+fi
+
+# Do this every time the package is installed or upgraded
+# Test for database presence to avoid failure of zmupdate.pl
+if [ "$dbc_install" = "true" ] && [ "$1" = "configure" ]; then
+
+ # Ensure zoneminder is stopped
+ deb-systemd-invoke stop zoneminder.service || exit $?
+
+ # Run the ZoneMinder update tool
+ zmupdate.pl --nointeractive
+
+fi
+
+#DEBHELPER#
+
+exit 0
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-core.postrm b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.postrm
new file mode 100644
index 000000000..d75e75e8b
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.postrm
@@ -0,0 +1,37 @@
+#! /bin/sh
+# postrm maintainer script for zoneminder-core package
+
+set -e
+
+# Source the debconf stuff
+if [ -f /usr/share/debconf/confmodule ]; then
+ . /usr/share/debconf/confmodule
+fi
+
+if [ "$1" = "purge" ]; then
+
+ # Ask the user if we have to remove the cache directory even if not empty
+ if [ -d /var/cache/zoneminder ] \
+ && [ ! $(find /var/cache/zoneminder -maxdepth 0 -type d -empty 2>/dev/null) ]; then
+ RET=""
+ db_input high zoneminder/ask_delete || true
+ db_go || true
+ db_get zoneminder/ask_delete
+ if [ "$RET" = "true" ]; then
+ RET=""
+ db_input high zoneminder/ask_delete_again || true
+ db_go || true
+ db_get zoneminder/ask_delete_again
+ if [ "$RET" = "true" ]; then
+ rm -rf /var/cache/zoneminder
+ fi
+ fi
+ fi
+fi
+
+#DEBHELPER#
+
+# postrm rm may freeze without that
+db_stop
+
+exit 0
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-core.preinst b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.preinst
new file mode 100644
index 000000000..3ed1ef661
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.preinst
@@ -0,0 +1,33 @@
+#!/bin/sh
+# preinst maintainer script for zoneminder-core package
+
+set -e
+
+abort=false
+if [ -L /usr/share/zoneminder/events ]; then
+ l=$(readlink /usr/share/zoneminder/events)
+ if [ "$l" != "/var/cache/zoneminder/events" ]; then
+ abort=true
+ fi
+fi
+if [ -L /usr/share/zoneminder/images ]; then
+ l=$(readlink /usr/share/zoneminder/images )
+ if [ "$l" != "/var/cache/zoneminder/images" ]; then
+ abort=true
+ fi
+fi
+
+if [ "$abort" = "true" ]; then
+ cat >&2 << EOF
+Aborting installation of zoneminder due to non-default symlinks in
+/usr/share/zoneminder for the images and/or events directory, which could
+result in loss of data. Please move your data in each of these directories to
+/var/cache/zoneminder before installing zoneminder from the package.
+EOF
+ exit 1
+
+fi
+
+#DEBHELPER#
+
+exit 0
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-core.templates b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.templates
new file mode 100644
index 000000000..35fdefd7a
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.templates
@@ -0,0 +1,19 @@
+Template: zoneminder/ask_delete
+Type: boolean
+Default: false
+_Description: Delete this non empty directory?
+ A purge of the ZoneMinder package is performed but the directory
+ '/var/cache/zoneminder' is not empty so it will not be deleted.
+ .
+ Please consider that this directory is designed to contain data resulting from
+ event detection. Therefore, "proof of evidence" could be lost!"
+ .
+ If you are not sure of your decision, please do not delete this directory but
+ perform a manual checkup.
+
+Template: zoneminder/ask_delete_again
+Type: boolean
+Default: false
+_Description: Deletion confirmed?
+ You have allowed the deletion of directory '/var/cache/zoneminder' although
+ it may contain critical data.
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-core.zoneminder.init b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.zoneminder.init
new file mode 100644
index 000000000..d3354c1d8
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.zoneminder.init
@@ -0,0 +1,90 @@
+#!/bin/sh
+### BEGIN INIT INFO
+# Provides: zoneminder
+# Required-Start: $network $remote_fs $syslog
+# Required-Stop: $network $remote_fs $syslog
+# Should-Start: mysql
+# Should-Stop: mysql
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: Control ZoneMinder as a Service
+### END INIT INFO
+# description: Control ZoneMinder as a Service
+# chkconfig: 2345 20 20
+
+# Source function library.
+#. /etc/rc.d/init.d/functions
+
+prog=ZoneMinder
+ZM_PATH_BIN="/usr/bin"
+RUNDIR=/var/run/zm
+TMPDIR=/tmp/zm
+command="$ZM_PATH_BIN/zmpkg.pl"
+
+start() {
+ echo -n "Starting $prog: "
+ mkdir -p $RUNDIR && chown www-data:www-data $RUNDIR
+ mkdir -p $TMPDIR && chown www-data:www-data $TMPDIR
+ $command start
+ RETVAL=$?
+ [ $RETVAL = 0 ] && echo success
+ [ $RETVAL != 0 ] && echo failure
+ echo
+ [ $RETVAL = 0 ] && touch /var/lock/zm
+ return $RETVAL
+}
+stop() {
+ echo -n "Stopping $prog: "
+ #
+ # Why is this status check being done?
+ # as $command stop returns 1 if zoneminder
+ # is stopped, which will result in
+ # this returning 1, which will stuff
+ # dpkg when it tries to stop zoneminder before
+ # uninstalling . . .
+ #
+ result=`$command status`
+ if [ ! "$result" = "running" ]; then
+ echo "Zoneminder already stopped"
+ echo
+ RETVAL=0
+ else
+ $command stop
+ RETVAL=$?
+ [ $RETVAL = 0 ] && echo success
+ [ $RETVAL != 0 ] && echo failure
+ echo
+ [ $RETVAL = 0 ] && rm -f /var/lock/zm
+ fi
+}
+status() {
+ result=`$command status`
+ if [ "$result" = "running" ]; then
+ echo "ZoneMinder is running"
+ RETVAL=0
+ else
+ echo "ZoneMinder is stopped"
+ RETVAL=1
+ fi
+}
+
+case "$1" in
+'start')
+ start
+ ;;
+'stop')
+ stop
+ ;;
+'restart' | 'force-reload')
+ stop
+ start
+ ;;
+'status')
+ status
+ ;;
+*)
+ echo "Usage: $0 { start | stop | restart | status }"
+ RETVAL=1
+ ;;
+esac
+exit $RETVAL
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-core.zoneminder.service b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.zoneminder.service
new file mode 100644
index 000000000..d82270024
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.zoneminder.service
@@ -0,0 +1,19 @@
+# ZoneMinder systemd unit file
+# This file is intended to work with debian distributions
+
+[Unit]
+Description=ZoneMinder CCTV recording and security system
+After=network.target mysql.service apache2.service
+Requires=apache2.service
+Wants=mysql.service
+
+[Service]
+User=www-data
+Type=forking
+ExecStart=/usr/bin/zmpkg.pl start
+ExecReload=/usr/bin/zmpkg.pl restart
+ExecStop=/usr/bin/zmpkg.pl stop
+PIDFile=/var/run/zm/zm.pid
+
+[Install]
+WantedBy=multi-user.target
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-core.zoneminder.tmpfile b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.zoneminder.tmpfile
new file mode 100644
index 000000000..6ea70bf35
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.zoneminder.tmpfile
@@ -0,0 +1 @@
+d /var/run/zm 0755 www-data www-data
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-database.config b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.config
new file mode 100644
index 000000000..f6a84d36d
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.config
@@ -0,0 +1,142 @@
+#!/bin/sh
+# config maintainer script for zoneminder
+
+set -e
+
+# Source the debconf stuff
+. /usr/share/debconf/confmodule
+
+# Set the first version in which dbconfig-common was introduced in the package
+dbc_first_version="1.28.0"
+
+CONFIGFILE=/etc/zm/zm.conf
+if [ -e $CONFIGFILE ]; then
+ # Source the config file if exists
+ . $CONFIGFILE
+elif [ -e ${CONFIGFILE}.dpkg-new ]; then
+ # If no config file, source the config file which is going to be installed
+ # by the core package
+ . ${CONFIGFILE}.dpkg-new
+else
+ # If no config file is going to be installed, set some default values
+ ZM_DB_HOST=
+ ZM_DB_NAME="zm"
+ ZM_DB_USER="zmuser"
+fi
+
+# Set some variables for the dbconfig-common stuff
+dbc_dbserver="$ZM_DB_HOST"
+dbc_dbname="$ZM_DB_NAME"
+dbc_dbuser="$ZM_DB_USER"
+
+if [ -f /usr/share/dbconfig-common/dpkg/config ]; then
+
+ # Default use dbconfig-common
+ dbc_install="true"
+
+ # Currently we only support mysql database
+ dbc_dbtypes="mysql"
+
+ # Set authentication method to password
+ dbc_authmethod_user="password"
+
+ # Source the dbconfig-common stuff
+ . /usr/share/dbconfig-common/dpkg/config
+fi
+
+# Do this when the package is installed, upgraded or reconfigured
+# Most of answers are cached so the questions will not be asked again
+if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then
+
+ # Ask the user if the database shall be installed locally or remotely
+ db_input high zoneminder/debconf_dblocation || true
+ db_go || true
+ db_get zoneminder/debconf_dblocation
+
+ if [ "$RET" = "local" ]; then
+ if [ ! -e "/usr/sbin/mysqld" ]; then
+ # Display a message and exit if the user want a local database but
+ # no database server is available
+ db_input high zoneminder/debconf_dblocalmissingerror || true
+ db_go || true
+ exit 0
+ else
+ # Set the database server to localhost
+ dbc_dbserver="localhost"
+ fi
+ else
+ # Source the dbconfig main configuration file
+ if [ -f /etc/dbconfig-common/config ]; then
+ . /etc/dbconfig-common/config
+ fi
+ if [ "$dbc_remote_questions_default" = "false" ]; then
+ # Display a message and exit if the dbconfig configuration does not
+ # allow installation of remote databases
+ # Note: To overcome this issue, we could think to override the
+ # default setting by using dbc_remote_questions_default='true' in
+ # maintainer scripts but unfortunately this does not work due to
+ # current dbconfig design
+ # More information here:
+ # https://bugs.launchpad.net/ubuntu/+source/dbconfig-common/+bug/1065331
+ db_input high zoneminder/debconf_dbconfigerror || true
+ db_go || true
+ exit 0
+ fi
+ fi
+
+ # Ask the user for all database settings
+ dbc_go zoneminder $@
+
+ # Ask the user for the password of the database administrator if the user
+ # has not yet answered to this question.
+ # This situation may occur if the user skipped the database creation step
+ # when reconfiguring the package.
+ RET=""
+ db_get zoneminder/mysql/admin-pass
+ if [ -z "$RET" ]; then
+ db_input high zoneminder/mysql/admin-pass || true
+ db_go || true
+ fi
+
+ # Do this only when not upgrading the package (no old version in argument)
+ if [ -z "$2" ]; then
+ # Ask for the password of 'admin' user
+ while :; do
+ RET=""
+ db_input high zoneminder/admin_password || true
+ db_go || true
+ db_get zoneminder/admin_password
+ # If password isn't empty we ask for password verification
+ if [ -z "$RET" ]; then
+ db_fset zoneminder/admin_password seen false
+ db_fset zoneminder/admin_password_again seen false
+ break
+ fi
+ ROOT_PW="$RET"
+ db_input high zoneminder/admin_password_again || true
+ db_go || true
+ db_get zoneminder/admin_password_again
+ if [ "$RET" = "$ROOT_PW" ]; then
+ ROOT_PW=""
+ break
+ fi
+ db_fset zoneminder/password_mismatch seen false
+ db_input critical zoneminder/password_mismatch || true
+ db_set zoneminder/admin_password ""
+ db_set zoneminder/admin_password_again ""
+ db_go || true
+ done
+ else
+ # If we are upgrading the package, set an empty password to disable
+ # password update in ZoneMinder database
+ db_set zoneminder/admin_password ""
+ fi
+ # Set the seen flag to not ask this question again if no password is
+ # provided
+ db_fset zoneminder/admin_password seen true
+
+fi
+
+#DEBHELPER#
+
+exit 0
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-database.dirs b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.dirs
new file mode 100644
index 000000000..b37463a9e
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.dirs
@@ -0,0 +1,3 @@
+usr/share/zoneminder/db
+usr/share/dbconfig-common/data/zoneminder/install
+usr/share/dbconfig-common/data/zoneminder/upgrade/mysql
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-database.install b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.install
new file mode 100644
index 000000000..756c5bbfa
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.install
@@ -0,0 +1 @@
+usr/share/zoneminder/db
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-database.postinst b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.postinst
new file mode 100644
index 000000000..41d4e5b5b
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.postinst
@@ -0,0 +1,79 @@
+#! /bin/sh
+# postinst maintainer script for zoneminder-db package
+
+set -e
+
+# Source the debconf stuff
+. /usr/share/debconf/confmodule
+
+mysql_update() {
+
+ # Source the dbconfig stuff
+ . /usr/share/dbconfig-common/internal/mysql
+
+ # Update the password of the hard-coded default 'admin' account
+ test -z $ADMIN_PASSWORD || dbc_mysql_exec_command "UPDATE Users SET Password = password('$ADMIN_PASSWORD') WHERE Username = 'admin';" || true
+
+ # Update the database version
+ dbc_mysql_exec_command "UPDATE Config SET Value = '$DB_VERSION' WHERE Name = 'ZM_DYN_DB_VERSION';" || true
+}
+
+if [ -f /usr/share/dbconfig-common/dpkg/postinst ]; then
+
+ # Set the first version in which dbconfig-common was introduced in the package
+ dbc_first_version="1.28.0"
+
+ # Set the database type
+ dbc_dbtypes="mysql"
+
+ # Source the dbconfig-common stuff
+ . /usr/share/dbconfig-common/dpkg/postinst
+fi
+
+# Do this when the package is installed, upgraded or reconfigured
+if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then
+
+ # Install sql database create file for dbconfig
+ # (needed at first package installation)
+ if [ ! -f /usr/share/dbconfig-common/data/zoneminder/install/mysql ]; then
+ install -m 644 /usr/share/zoneminder/db/zm_create.sql \
+ /usr/share/dbconfig-common/data/zoneminder/install/mysql
+ # Remove unneeded sql requests
+ # dbconfig will create the underlying database
+ sed -i "/^ *CREATE DATABASE /d" \
+ /usr/share/dbconfig-common/data/zoneminder/install/mysql
+ sed -i "/^ *USE /d" \
+ /usr/share/dbconfig-common/data/zoneminder/install/mysql
+ fi
+
+ # Symlink sql update files for dbconfig (needed when upgrading the package)
+ for sqlfile in /usr/share/zoneminder/db/zm_update-*.sql; do
+ lnk=`echo $sqlfile | sed "s/^\/usr\/share\/zoneminder\/db\/zm_update-\(.*\)\.sql/\1/"`
+ if [ ! -L /usr/share/dbconfig-common/data/zoneminder/upgrade/mysql/$lnk ]; then
+ ln -sf $sqlfile \
+ /usr/share/dbconfig-common/data/zoneminder/upgrade/mysql/$lnk
+ fi
+ done || true
+
+ # Create the underlying database and populate it
+ # dbconfig will take care of applying any updates which are newer than the
+ # previously installed version
+ dbc_go zoneminder $@
+
+ # Get the password of ZoneMinder user 'admin' from debconf
+ db_get zoneminder/admin_password
+ ADMIN_PASSWORD=$RET
+
+ # Remove the password from debconf database
+ test -z $ADMIN_PASSWORD || db_reset zoneminder/admin_password || true
+
+ # Get the lastest database version from dbconfig upgrade folder
+ DB_VERSION=$(ls -rv /usr/share/dbconfig-common/data/zoneminder/upgrade/$dbc_dbtypes | head -1)
+
+ # Update the default admin account and database version
+ mysql_update
+fi
+
+#DEBHELPER#
+
+exit 0
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-database.postrm b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.postrm
new file mode 100644
index 000000000..231f01ad7
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.postrm
@@ -0,0 +1,34 @@
+#! /bin/sh
+# postrm maintainer script for zoneminder-db package
+
+set -e
+
+# Source the debconf stuff
+if [ -f /usr/share/debconf/confmodule ]; then
+ . /usr/share/debconf/confmodule
+fi
+
+# Source the dbconfig stuff
+if [ -f /usr/share/dbconfig-common/dpkg/postrm ]; then
+ . /usr/share/dbconfig-common/dpkg/postrm
+ # Ask the user what do to with dbconfig when removing the package
+ dbc_go zoneminder $@
+fi
+
+if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then
+ # Remove dbconfig stuff added in postinst script
+ rm -rf /usr/share/dbconfig-common/data/zoneminder
+ # No need to manually remove the zm database, dbconfig take care of this
+fi
+
+if [ "$1" = "purge" ]; then
+ # Delete a potential remaining file used in postinst script
+ rm -f /etc/zm/zm.conf.postinst.bak
+fi
+
+#DEBHELPER#
+
+# postrm rm may freeze without that
+db_stop
+
+exit 0
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-database.prerm b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.prerm
new file mode 100644
index 000000000..31786116a
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.prerm
@@ -0,0 +1,22 @@
+#!/bin/sh
+# prerm script for zoneminder-db package
+
+set -e
+
+# Source the debconf stuff if file exists
+if [ -f /usr/share/debconf/confmodule ]; then
+ . /usr/share/debconf/confmodule
+fi
+
+# If dbconfig-common is installed and has been used by zoneminder
+if [ -f /usr/share/dbconfig-common/dpkg/prerm ] \
+ && [ -f /etc/dbconfig-common/zoneminder.conf ]; then
+ # Source the dbconfig stuff
+ . /usr/share/dbconfig-common/dpkg/prerm
+ # Ask the user what do to with dbconfig before removing the package
+ dbc_go zoneminder $@
+fi
+
+# #DEBHELPER#
+
+exit 0
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-database.templates b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.templates
new file mode 100644
index 000000000..4de4342f6
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.templates
@@ -0,0 +1,58 @@
+Template: zoneminder/debconf_dblocation
+Type: select
+__Choices: local, remote
+Default: local
+_Description: Database location:
+ A database server is required to run ZoneMinder. The database can be installed
+ either locally or remotely on a machine of your network.
+ .
+ If you choose a remote location, you will have to select the 'tcp/ip'
+ connection method and enter the hostname or ip address of the remote machine
+ in the next configuration screens.
+
+Template: zoneminder/debconf_dblocalmissingerror
+Type: error
+_Description: No local database server is available:
+ Currently ZoneMinder supports mysql or mariadb database server but none of them
+ appears to be installed on this machine.
+ .
+ In order to complete ZoneMinder's installation, after ending of this assistant,
+ please install a compatible database server and then restart the assistant by
+ invoking:
+ .
+ $ sudo dpkg-reconfigure zoneminder
+
+Template: zoneminder/debconf_dbconfigerror
+Type: error
+_Description: Remote database servers are not allowed:
+ The current configuration of dbconfig-common does not allow installation of
+ a database on remote servers.
+ .
+ In order to reconfigure dbconfig-common, please invoke the following command
+ after ending of this assistant:
+ .
+ $ sudo dpkg-reconfigure dbconfig-common
+ .
+ Then, to complete ZoneMinder's installation, please restart this assistant by
+ invoking:
+ .
+ $ sudo dpkg-reconfigure zoneminder
+
+Template: zoneminder/admin_password
+Type: password
+_Description: New password for the ZoneMinder 'admin' user:
+ Please enter the password of the default administrative user.
+ .
+ While not mandatory, it is highly recommended that you set a custom password
+ for the administrative 'admin' user.
+ .
+ If this field is left blank, the password will not be changed.
+
+Template: zoneminder/admin_password_again
+Type: password
+_Description: Repeat password for the ZoneMinder 'admin' user:
+
+Template: zoneminder/password_mismatch
+Type: error
+_Description: Password input error
+ The two passwords you entered were not the same. Please try again.
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-api.install b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-api.install
new file mode 100644
index 000000000..5759ebc85
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-api.install
@@ -0,0 +1 @@
+usr/share/zoneminder/api
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.config b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.config
new file mode 100644
index 000000000..2660208a8
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.config
@@ -0,0 +1,20 @@
+#!/bin/sh
+# config maintainer script for zoneminder-ui-base package
+
+set -e
+
+# Source the debconf stuff
+. /usr/share/debconf/confmodule
+
+# Do this when the package is installed, upgraded or reconfigured
+# Most of answers are cached so the questions will not be asked again
+if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then
+
+ # Ask the user for the web server(s) to configure
+ db_input high zoneminder/webserver || true
+ db_go || true
+fi
+
+#DEBHELPER#
+
+exit 0
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.install b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.install
new file mode 100644
index 000000000..f72b569be
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.install
@@ -0,0 +1,11 @@
+debian/apache.conf etc/zm
+usr/lib/cgi-bin
+usr/share/zoneminder/ajax
+usr/share/zoneminder/css
+usr/share/zoneminder/graphics
+usr/share/zoneminder/includes
+usr/share/zoneminder/index.php
+usr/share/zoneminder/js
+usr/share/zoneminder/lang
+usr/share/zoneminder/tools
+usr/share/zoneminder/views
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.links b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.links
new file mode 100644
index 000000000..b00a147d6
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.links
@@ -0,0 +1 @@
+usr/lib/cgi-bin usr/share/zoneminder/cgi-bin
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.postinst b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.postinst
new file mode 100644
index 000000000..a5bce3c98
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.postinst
@@ -0,0 +1,48 @@
+#! /bin/sh
+# postinst maintainer script for zoneminder-ui-base package
+
+set -e
+
+# Source the debconf stuff
+. /usr/share/debconf/confmodule
+
+apache_install() {
+
+ mkdir -p /etc/apache2/conf-available
+ ln -sf ../../zm/apache.conf /etc/apache2/conf-available/zoneminder.conf
+
+ COMMON_STATE=$(dpkg-query -f '${Status}' -W 'apache2.2-common' 2>/dev/null | awk '{print $3}' || true)
+
+ if [ -e /usr/share/apache2/apache2-maintscript-helper ] ; then
+ . /usr/share/apache2/apache2-maintscript-helper
+ apache2_invoke enconf zoneminder
+ elif [ "$COMMON_STATE" = "installed" ] || [ "$COMMON_STATE" = "unpacked" ] ; then
+ [ -d /etc/apache2/conf.d/ ] && [ ! -L /etc/apache2/conf.d/zoneminder.conf ] && ln -s ../conf-available/zoneminder.conf /etc/apache2/conf.d/zoneminder.conf
+ fi
+
+ # Enable CGI script module in apache (not enabled by default on jessie)
+ a2enmod cgi >/dev/null 2>&1
+
+ # Reload the web server
+ deb-systemd-invoke reload apache2.service || true
+}
+
+# Do this when the package is installed, upgraded or reconfigured
+if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then
+
+ # Configure the web server
+ db_get zoneminder/webserver
+ webservers="$RET"
+
+ for webserver in $webservers; do
+ webserver=${webserver%,}
+ # Currently we only support apache2
+ if [ "$webserver" = "apache2" ] ; then
+ apache_install $1
+ fi
+ done
+fi
+
+#DEBHELPER#
+
+exit 0
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.postrm b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.postrm
new file mode 100644
index 000000000..441bb5218
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.postrm
@@ -0,0 +1,41 @@
+#! /bin/sh
+# postrm maintainer script for zoneminder-ui-base package
+
+set -e
+
+# Source the debconf stuff
+if [ -f /usr/share/debconf/confmodule ]; then
+ . /usr/share/debconf/confmodule
+fi
+
+apache_remove() {
+ COMMON_STATE=$(dpkg-query -f '${Status}' -W 'apache2.2-common' 2>/dev/null | awk '{print $3}' || true)
+ if [ -e /usr/share/apache2/apache2-maintscript-helper ] ; then
+ . /usr/share/apache2/apache2-maintscript-helper
+ apache2_invoke disconf zoneminder
+ elif [ "$COMMON_STATE" = "installed" ] || [ "$COMMON_STATE" = "unpacked" ] ; then
+ rm -f /etc/apache2/conf.d/zoneminder.conf
+ fi
+ rm -f /etc/apache2/conf-available/zoneminder.conf
+ # Reload the web server
+ deb-systemd-invoke reload apache2.service || true
+}
+
+if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then
+ # Deconfigure the web server
+ db_get zoneminder/webserver
+ for webserver in $RET; do
+ webserver=${webserver%,}
+ # Currently we only support apache2
+ if [ "$webserver" = "apache2" ] ; then
+ apache_remove $1
+ fi
+ done
+fi
+
+#DEBHELPER#
+
+# postrm rm may freeze without that
+db_stop
+
+exit 0
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.templates b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.templates
new file mode 100644
index 000000000..31e70277f
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.templates
@@ -0,0 +1,7 @@
+Template: zoneminder/webserver
+Type: multiselect
+Choices: apache2
+Default: apache2
+_Description: Web server to reconfigure automatically:
+ Please choose the web server that should be automatically configured for
+ ZoneMinder's web portal access.
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-classic.install b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-classic.install
new file mode 100644
index 000000000..9532d9dc9
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-classic.install
@@ -0,0 +1 @@
+usr/share/zoneminder/skins/classic
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-mobile.install b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-mobile.install
new file mode 100644
index 000000000..464bb74eb
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-mobile.install
@@ -0,0 +1 @@
+usr/share/zoneminder/skins/mobile
diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-xml.install b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-xml.install
new file mode 100644
index 000000000..6617707f8
--- /dev/null
+++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-xml.install
@@ -0,0 +1 @@
+usr/share/zoneminder/skins/xml
diff --git a/docs/api.rst b/docs/api.rst
index 26b6be251..c5baaafb8 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -1,10 +1,10 @@
API
-===
+^^^
-This document will provide an overview of ZoneMinder's API.
+This document will provide an overview of ZoneMinder's API. This is work in progress.
Overview
---------
+^^^^^^^^
In an effort to further 'open up' ZoneMinder, an API was needed. This will
allow quick integration with and development of ZoneMinder.
@@ -14,7 +14,7 @@ provides a RESTful service and supports CRUD (create, retrieve, update, delete)
functions for Monitors, Events, Frames, Zones and Config.
Examples
---------
+^^^^^^^^
Here be a list of examples. Some results may be truncated.
@@ -22,97 +22,244 @@ 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)
+
+API Version
+^^^^^^^^^^^
+To retrieve the API version:
+::
+ http://server/zm/api/host/getVersion.json
+
+
Return a list of all monitors
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-``curl -XGET http://zmdevapi/monitors.json``
+::
+
+ curl -XGET http://server/zm/api/monitors.json
Retrieve monitor 1
-^^^^^^^^^^^^^^^^^^
-``curl -XGET http://zmdevapi/monitors/1.json``
+^^^^^^^^^^^^^^^^^^^
+
+ ::
+
+ curl -XGET http://server/zm/api/monitors/1.json
+
+
+Change State of Monitor 1
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This API changes monitor 1 to Modect and Enabled
+::
+
+ curl -XPOST http://server/zm/api/monitors/1.json -d "Monitor[Function]=Modect&Monitor[Enabled]:true"
Add a monitor
-^^^^^^^^^^^^^
+^^^^^^^^^^^^^^
This command will add a new http monitor.
-``curl -XPOST http://zmdevapi/monitors.js -d "Monitor[Name]=Cliff-Burton \
-&Monitor[Function]=Modect \
-&Monitor[Protocol]=http \
-&Monitor[Method]=simple \
-&Monitor[Host]=ussr:pass@192.168.11.20 \
-&Monitor[Port]=80 \
-&Monitor[Path]=/mjpg/video.mjpg \
-&Monitor[Width]=704 \
-&Monitor[Height]=480 \
-&Monitor[Colours]=4"``
+::
+
+ curl -XPOST http://server/zm/api/monitors.json -d "Monitor[Name]=Cliff-Burton \
+ &Monitor[Function]=Modect \
+ &Monitor[Protocol]=http \
+ &Monitor[Method]=simple \
+ &Monitor[Host]=usr:pass@192.168.11.20 \
+ &Monitor[Port]=80 \
+ &Monitor[Path]=/mjpg/video.mjpg \
+ &Monitor[Width]=704 \
+ &Monitor[Height]=480 \
+ &Monitor[Colours]=4"
Edit monitor 1
-^^^^^^^^^^^^^^
+^^^^^^^^^^^^^^^
This command will change the 'Name' field of Monitor 1 to 'test1'
-``curl -XPUT http://zmdevapi/monitors/1.json -d "Monitor[Name]=test1"``
+::
+
+ curl -XPUT http://server/zm/api/monitors/1.json -d "Monitor[Name]=test1"
+
Delete monitor 1
-^^^^^^^^^^^^^^^^
+^^^^^^^^^^^^^^^^^
This command will delete Monitor 1, but will _not_ delete any Events which
depend on it.
+::
+
+ curl -XDELETE http://server/zm/api/monitors/1.json
-``curl -XDELETE http://zmdevapi/monitors/1.json``
Return a list of all events
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-``curl -XGET http://zmdevapi/events.json``
+::
+
+ curl -XGET http://server/zm/api/events.json
+
+
+Note that events list can be quite large and this API (as with all other APIs in ZM)
+uses pagination. Each page returns a specific set of entries. By default this is 25
+and ties into WEB_EVENTS_PER_PAGE in the ZM options menu.
+
+So the logic to iterate through all events should be something like this (pseudocode):
+(unfortunately there is no way to get pageCount without getting the first page)
+
+::
+
+ data = http://server/zm/api/events.json?page=1 # this returns the first page
+ # The json object returned now has a property called data.pagination.pageCount
+ count = data.pagination.pageCount;
+ for (i=1, i=:2015-05-15 18:43:56/EndTime <=:2015-05-16 18:43:56.json
+
+
+To try this in CuRL, you need to URL escape the spaces like so:
+
+::
+
+ curl -XGET "http://server/zm/api/events/index/MonitorId:5/StartTime%20>=:2015-05-15%2018:43:56/EndTime%20<=:2015-05-16%2018:43:56.json"
+
+
+Return a list of events for all monitors within a specified date/time range
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+ curl -XGET "http://server/zm/api/events/index/StartTime%20>=:2015-05-15%2018:43:56/EndTime%20<=:208:43:56.json"
+
+
+
+Configuration Apis
+^^^^^^^^^^^^^^^^^^^
+
+The APIs allow you to access all the configuration parameters of ZM that you typically set inside the web console.
+This returns the full list of configuration parameters:
+
+::
+
+ curl -XGET http://server/zm/api/configs.json
+
+
+Each configuration parameter has an Id, Name, Value and other fields. Chances are you are likely only going to focus on these 3.
+
+(Example of changing config TBD)
+
+Run State Apis
^^^^^^^^^^^^^^^
-This command will change the 'Value' field of Config 121 to 901.
+ZM API can be used to start/stop/restart/list states of ZM as well
+Examples:
+
+::
+
+ curl -XGET http://server/zm/api/states.json # returns list of run states
+ curl -XPOST http://server/zm/api/states/change/restart.json #restarts ZM
+ curl -XPOST http://server/zm/api/states/change/stop.json #Stops ZM
+ curl -XPOST http://server/zm/api/states/change/start.json #Starts ZM
+
-``curl -XPUT http://zmdevapi/configs/121.json -d "Config[Value]=901"``
Create a Zone
-^^^^^^^^^^^^^
+^^^^^^^^^^^^^^
+
+::
+
+ curl -XPOST http://server/zm/api/zones.json -d "Zone[Name]=Jason-Newsted \
+ &Zone[MonitorId]=3 \
+ &Zone[Type]=Active \
+ &Zone[Units]=Percent \
+ &Zone[NumCoords]=4 \
+ &Zone[Coords]=0,0 639,0 639,479 0,479 \
+ &Zone[AlarmRGB]=16711680 \
+ &Zone[CheckMethod]=Blobs \
+ &Zone[MinPixelThreshold]=25 \
+ &Zone[MaxPixelThreshold]= \
+ &Zone[MinAlarmPixels]=9216 \
+ &Zone[MaxAlarmPixels]= \
+ &Zone[FilterX]=3 \
+ &Zone[FilterY]=3 \
+ &Zone[MinFilterPixels]=9216 \
+ &Zone[MaxFilterPixels]=230400 \
+ &Zone[MinBlobPixels]=6144 \
+ &Zone[MaxBlobPixels]= \
+ &Zone[MinBlobs]=1 \
+ &Zone[MaxBlobs]= \
+ &Zone[OverloadFrames]=0"
+
+PTZ Control 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
+
+This returns all the control definitions:
+::
+
+ curl http://server/zm/api/controls.json
+
+This returns control definitions for a specific control ID=5
+::
+
+ curl http://server/zm/api/controls/5.json
+
+Host APIs
+^^^^^^^^^^
+
+ZM APIs have various APIs that help you in determining host (aka ZM) daemon status, load etc. Some examples:
+
+::
+
+ 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) ``
-``curl -XPOST http://zmdevapi/zones.json -d "Zone[Name]=Jason-Newsted \
-&Zone[MonitorId]=3 \
-&Zone[Type]=Active \
-&Zone[Units]=Percent \
-&Zone[NumCoords]=4 \
-&Zone[Coords]=0,0 639,0 639,479 0,479 \
-&Zone[AlarmRGB]=16711680 \
-&Zone[CheckMethod]=Blobs \
-&Zone[MinPixelThreshold]=25 \
-&Zone[MaxPixelThreshold]= \
-&Zone[MinAlarmPixels]=9216 \
-&Zone[MaxAlarmPixels]= \
-&Zone[FilterX]=3 \
-&Zone[FilterY]=3 \
-&Zone[MinFilterPixels]=9216 \
-&Zone[MaxFilterPixels]=230400 \
-&Zone[MinBlobPixels]=6144 \
-&Zone[MaxBlobPixels]= \
-&Zone[MinBlobs]=1 \
-&Zone[MaxBlobs]= \
-&Zone[OverloadFrames]=0"``
diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt
index 736e933e5..e9dd6ddd3 100644
--- a/scripts/CMakeLists.txt
+++ b/scripts/CMakeLists.txt
@@ -24,6 +24,12 @@ endif(NOT ZM_NO_X10)
configure_file(zm.in "${CMAKE_CURRENT_BINARY_DIR}/zm" @ONLY)
#configure_file(zmeventdump.in zmeventdump @ONLY)
+# Generate man files for the perl scripts destined for the bin folder
+file(GLOB perlscripts RELATIVE "${CMAKE_CURRENT_BINARY_DIR}" "*.pl")
+FOREACH(PERLSCRIPT ${perlscripts})
+ POD2MAN(${CMAKE_CURRENT_SOURCE_DIR}/${PERLSCRIPT} zoneminder-${PERLSCRIPT} 8)
+ENDFOREACH(PERLSCRIPT ${perlscripts})
+
# Install the perl scripts
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmaudit.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcontrol.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmdc.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmfilter.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmpkg.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrack.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrigger.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmupdate.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmvideo.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmwatch.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcamtool.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmsystemctl.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
if(NOT ZM_NO_X10)
diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/FI8918W.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/FI8918W.pm
new file mode 100644
index 000000000..835f2943b
--- /dev/null
+++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/FI8918W.pm
@@ -0,0 +1,355 @@
+# Modified on Jun 19 2016 by PP
+# Changes made
+# - modified command to work properly and pick up credentials from Control Device
+# - the old script did not stop moving- added autostop
+# (note that mjpeg cameras have onestep but that is too granular)
+# - You need to set "user=xxx&pwd=yyy" in the ControlDevice field (NOT usr like in Foscam HD)
+
+# ==========================================================================
+#
+# ZoneMinder Foscam FI8918W IP Control Protocol Module, $Date: 2009-11-25 09:20:00 +0000 (Wed, 04 Nov 2009) $, $Revision: 0001 $
+# Copyright (C) 2001-2008 Philip Coombes
+# Modified for use with Foscam FI8918W IP Camera by Dave Harris
+# Modified Feb 2011 by Howard Durdle (http://durdl.es/x) to:
+# fix horizontal panning, add presets and IR on/off
+# use Control Device field to pass username and password
+# Modified May 2014 by Arun Horne (http://arunhorne.co.uk) to:
+# use HTTP basic auth as required by firmware 11.37.x.x upward
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# ==========================================================================
+#
+# This module contains the implementation of the Foscam FI8918W IP camera control
+# protocol
+#
+
+package MyAgent;
+
+use base 'LWP::UserAgent';
+
+
+package ZoneMinder::Control::FI8918W;
+
+use 5.006;
+use strict;
+use warnings;
+
+require ZoneMinder::Base;
+require ZoneMinder::Control;
+
+our @ISA = qw(ZoneMinder::Control);
+
+our $VERSION = $ZoneMinder::Base::VERSION;
+
+# ==========================================================================
+#
+# Foscam FI8918W IP Control Protocol
+#
+# ==========================================================================
+
+use ZoneMinder::Logger qw(:all);
+use ZoneMinder::Config qw(:all);
+
+ use Time::HiRes qw( usleep );
+
+sub new
+{
+ my $class = shift;
+ my $id = shift;
+ my $self = ZoneMinder::Control->new( $id );
+ my $logindetails = "";
+ bless( $self, $class );
+ srand( time() );
+ return $self;
+}
+
+our $AUTOLOAD;
+
+sub AUTOLOAD
+{
+ my $self = shift;
+ my $class = ref($self) || croak( "$self not object" );
+ my $name = $AUTOLOAD;
+ $name =~ s/.*://;
+ if ( exists($self->{$name}) )
+ {
+ return( $self->{$name} );
+ }
+ Fatal( "Can't access $name member of object of class $class" );
+}
+our $stop_command;
+
+sub open
+{
+ my $self = shift;
+
+ $self->loadMonitor();
+
+ $self->{ua} = MyAgent->new;
+ $self->{ua}->agent( "ZoneMinder Control Agent/" );
+
+ $self->{state} = 'open';
+}
+
+sub close
+{
+ my $self = shift;
+ $self->{state} = 'closed';
+}
+
+sub printMsg
+{
+ my $self = shift;
+ my $msg = shift;
+ my $msg_len = length($msg);
+
+ Debug( $msg."[".$msg_len."]" );
+}
+
+sub sendCmd
+{
+ my $self = shift;
+ my $cmd = shift;
+ my $result = undef;
+ printMsg( $cmd, "Tx" );
+
+ # PP Old cameras also support onstep=1 but it is too granular. Instead using moveCon and stop after interval
+ # PP - cleaned up URL to take it properly from Control device
+ # Control device needs to be of format user=xxx&pwd=yyy
+ my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/$cmd"."&".$self->{Monitor}->{ControlDevice});
+ print ("Sending $req\n");
+ my $res = $self->{ua}->request($req);
+
+ if ( $res->is_success )
+ {
+ $result = !undef;
+ }
+ else
+ {
+ Error( "Error REALLY check failed:'".$res->status_line()."'" );
+ Error ("Cmd:".$req);
+ }
+
+ return( $result );
+}
+
+sub reset
+{
+ my $self = shift;
+ Debug( "Camera Reset" );
+ my $cmd = "reboot.cgi?";
+ $self->sendCmd( $cmd );
+}
+
+# PP - in all move operations, added auto stop after timeout
+
+#Up Arrow
+sub moveConUp
+{
+ my $self = shift;
+ Debug( "Move Up" );
+ my $cmd = "decoder_control.cgi?command=0";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+#Down Arrow
+sub moveConDown
+{
+ my $self = shift;
+ Debug( "Move Down" );
+ my $cmd = "decoder_control.cgi?command=2";
+ $self->sendCmd( $cmd );
+}
+
+#Left Arrow
+sub moveConLeft
+{
+ my $self = shift;
+ Debug( "Move Left" );
+ my $cmd = "decoder_control.cgi?command=6";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+#Right Arrow
+sub moveConRight
+{
+ my $self = shift;
+ Debug( "Move Right" );
+ my $cmd = "decoder_control.cgi?command=4";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+#Diagonally Up Right Arrow
+sub moveConUpRight
+{
+ my $self = shift;
+ Debug( "Move Diagonally Up Right" );
+ my $cmd = "decoder_control.cgi?command=90";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+
+}
+
+#Diagonally Down Right Arrow
+sub moveConDownRight
+{
+ my $self = shift;
+ Debug( "Move Diagonally Down Right" );
+ my $cmd = "decoder_control.cgi?command=92";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+#Diagonally Up Left Arrow
+sub moveConUpLeft
+{
+ my $self = shift;
+ Debug( "Move Diagonally Up Left" );
+ my $cmd = "decoder_control.cgi?command=91";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+#Diagonally Down Left Arrow
+sub moveConDownLeft
+{
+ my $self = shift;
+ Debug( "Move Diagonally Down Left" );
+ my $cmd = "decoder_control.cgi?command=93";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+#Stop
+sub moveStop
+{
+ my $self = shift;
+ Debug( "Move Stop" );
+ my $cmd = "decoder_control.cgi?command=1";
+ $self->sendCmd( $cmd );
+}
+
+# PP - imported from 9831 - autostop after usleep
+sub autoStop
+{
+ my $self = shift;
+ my $autostop = shift;
+ if( $autostop )
+ {
+ Debug( "Auto Stop" );
+ usleep( $autostop );
+ my $cmd = "decoder_control.cgi?command=1";
+ $self->sendCmd( $cmd );
+ }
+}
+
+#Move Camera to Home Position
+sub presetHome
+{
+ my $self = shift;
+ Debug( "Home Preset" );
+ my $cmd = "decoder_control.cgi?command=25";
+ $self->sendCmd( $cmd );
+}
+
+#Set preset
+sub presetSet
+{
+ my $self = shift;
+ my $params = shift;
+ my $preset = $self->getParam( $params, 'preset' );
+ my $presetCmd = 30 + ($preset*2);
+ Debug( "Set Preset $preset with cmd $presetCmd" );
+ my $cmd = "decoder_control.cgi?command=$presetCmd";
+ $self->sendCmd( $cmd );
+}
+
+#Goto preset
+sub presetGoto
+{
+ my $self = shift;
+ my $params = shift;
+ my $preset = $self->getParam( $params, 'preset' );
+ my $presetCmd = 31 + ($preset*2);
+ Debug( "Goto Preset $preset with cmd $presetCmd" );
+ my $cmd = "decoder_control.cgi?command=$presetCmd";
+ $self->sendCmd( $cmd );
+}
+
+#Turn IR on
+sub wake
+{
+ my $self = shift;
+ Debug( "Wake - IR on" );
+ my $cmd = "decoder_control.cgi?command=95";
+ $self->sendCmd( $cmd );
+}
+
+#Turn IR off
+sub sleep
+{
+ my $self = shift;
+ Debug( "Sleep - IR off" );
+ my $cmd = "decoder_control.cgi?command=94";
+ $self->sendCmd( $cmd );
+}
+
+1;
+__END__
+
+=head1 FI8918W
+
+ZoneMinder::Database - Perl extension for FOSCAM FI8918W
+
+=head1 SYNOPSIS
+
+Control script for Foscam MJPEG 8918W cameras.
+
+=head1 DESCRIPTION
+
+You need to set "user=xxx&pwd=yyy" in the ControlDevice field
+of the control tab for that monitor.
+Auto TimeOut should be 1. Don't set it to less - processes
+start crashing :)
+NOTE: unlike HD foscam cameras, this one uses "user" not "usr"
+in the control device
+
+=head2 EXPORT
+
+None by default.
+
+
+
+=head1 SEE ALSO
+
+=head1 AUTHOR
+
+Philip Coombes, Ephilip.coombes@zoneminder.comE
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2001-2008 Philip Coombes
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself, either Perl version 5.8.3 or,
+at your option, any later version of Perl 5 you may have available.
+
+
+=cut
+
diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/FI9831W.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/FI9831W.pm
new file mode 100644
index 000000000..4a7bab230
--- /dev/null
+++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/FI9831W.pm
@@ -0,0 +1,763 @@
+# Modified by PP to clean up user/auth dependencies inside the script
+# Also, you can specify your auth credentials in the Control tab of the monitor
+# In "ControlDevice" put in
+# usr=xxxx&pwd=xxx
+# where xxx is the auth credentials to your foscam camera
+# The Foscam CGI manual referred to was v1.0.10
+# All other notices below may be stale
+#
+# ==========================================================================
+#
+# ZoneMinder FOSCAM version 1.0 API Control Protocol Module, $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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# =========================================================================================
+#
+# This module FI8620_Y2k.pm contains the implementation of API camera control
+# For FOSCAM FI8620 Dome PTZ Camera (This cam support only H264 streaming)
+# V1.0 Le 09 AOUT 2013 - production usable for the script but not for the camera "reboot itself"
+# If you wan't to contact me i understand French and English, precise ZoneMinder in subject
+# My name is Christophe DAPREMONT my email is christophe_y2k@yahoo.fr
+#
+# =========================================================================================
+#
+package ZoneMinder::Control::FI9831W;
+
+use 5.006;
+use strict;
+use warnings;
+
+require ZoneMinder::Base;
+require ZoneMinder::Control;
+
+our @ISA = qw(ZoneMinder::Control);
+# ===================================================================================================================================
+#
+# FI9821 FOSCAM PT H264 Control Protocol
+# with Firmware version V1.2.1.1 (latest at 09/08/2013)
+# based with the latest buggy CGI doc from FOSCAM ( http://foscam.us/forum/cgi-sdk-for-hd-camera-t6045.html )
+# This IPCAM work under ZoneMinder V1.25 from alternative source of code
+# from this svn at https://svn.unixmedia.net/public/zum/trunk/zum/
+# Many Thanks to "MASTERTHEKNIFE" for the excellent speed optimisation ( http://www.zoneminder.com/forums/viewtopic.php?f=9&t=17652 )
+# And to "NEXTIME" for the recent source update and incredible plugins ( http://www.zoneminder.com/forums/viewtopic.php?f=9&t=20587 )
+# And all people helping ZoneMinder dev.
+#
+# -FUNCTION: display on OSD
+# speed is progressive in function of where you click on arrow ========>
+# speed low=/ \=speed high
+# ===================================================================================================================================
+use ZoneMinder::Logger qw(:all);
+use ZoneMinder::Config qw(:all);
+use Time::HiRes qw( usleep );
+
+# Set $osd to "off" if you wan't disabled OSD i need to place this variable in another script because
+# this script is reload at every command ,if i want the button on/off (Focus MAN) for OSD works...
+# PP - changed this to off - it achieves OSD by renaming the Device and what happens is at times
+# it does not reset the name if a command fails. Net result: Your camera gets a name like "Move Left" which
+# I bet you won't like
+my $osd = "off";
+my $cmd;
+
+sub new
+{
+ my $class = shift;
+ my $id = shift;
+ my $self = ZoneMinder::Control->new( $id );
+ bless( $self, $class );
+ srand( time() );
+ return $self;
+}
+
+our $AUTOLOAD;
+
+sub AUTOLOAD
+{
+ my $self = shift;
+ my $class = ref($self) || croak( "$self not object" );
+ my $name = $AUTOLOAD;
+ $name =~ s/.*://;
+ if ( exists($self->{$name}) )
+ {
+ return( $self->{$name} );
+ }
+ Fatal( "Can't access $name member of object of class $class" );
+}
+
+sub open
+{
+ my $self = shift;
+ $self->loadMonitor();
+ use LWP::UserAgent;
+ $self->{ua} = LWP::UserAgent->new;
+ #PP
+ #$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
+ $self->{ua}->agent( "ZoneMinder Control Agent/" );
+ $self->{state} = 'open';
+}
+
+sub close
+{
+ my $self = shift;
+ $self->{state} = 'closed';
+}
+
+sub printMsg
+{
+ my $self = shift;
+ my $msg = shift;
+ my $msg_len = length($msg);
+ Debug( $msg."[".$msg_len."]" );
+}
+
+sub sendCmd
+{
+ my $self = shift;
+ my $cmd = shift;
+ my $result = undef;
+ printMsg( $cmd, "Tx" );
+ my $temps = time();
+ #PP - cleaned this up so it picks up the full auth from Control Devices
+ my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/cgi-bin/CGIProxy.fcgi?cmd=".$cmd."&".$self->{Monitor}->{ControlDevice} );
+ #my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/cgi-bin/CGIProxy.fcgi?usr%3Dadmin%26pwd%3D".$self->{Monitor}->{ControlDevice}."%26cmd%3D".$cmd."%26".$temps );
+ my $res = $self->{ua}->request($req);
+ if ( $res->is_success )
+ {
+ $result = !undef;
+ }
+ else
+ {
+ Error( "Error check failed: '".$res->status_line()."'" );
+ }
+ return( $result );
+}
+
+# PP - changed this to a system reboot. Its harmful to reset here. Settings may change
+# with different firmware versions. Better to make this a reboot and use the camera
+# interface to reset streams
+sub reset
+{ my $self = shift;
+ Debug ( "Reboot= setup camera FoscamHD" );
+ $cmd = "rebootSystem";
+ #my $cmd = "setOSDSetting%26isEnableTimeStamp%3D0%26isEnableDevName%3D1%26dispPos%3D0%26isEnabledOSDMask%3D0";
+ Info ("Sending reboot $cmd");
+ $self->sendCmd( $cmd );
+ # Setup For Stream=0 Resolution=720p Bandwith=4M FPS=30 KeyFrameInterval/GOP=100 VBR=ON
+ #$cmd = "setVideoStreamParam%26streamType%3D0%26resolution%3D0%26bitRate%3D4194304%26frameRate%3D30%26GOP%3D100%26isVBR%3D1";
+ #$self->sendCmd( $cmd );
+ # Setup For Infrared AUTO
+ #$cmd = "setInfraLedConfig%26Mode%3D1";
+ #$self->sendCmd( $cmd );
+ # Reset image settings
+ #$cmd = "resetImageSetting";
+ #$self->sendCmd( $cmd );
+}
+
+sub moveStop
+{
+ my $self = shift;
+ Debug( "Move Stop" );
+ my $cmd = "ptzStopRun";
+ $self->sendCmd( $cmd );
+ if ($osd eq "on")
+ {
+ $cmd = "setDevName%26devName%3D.";
+ $self->sendCmd( $cmd );
+ $cmd = "setOSDSetting%26isEnableDevName%3D1";
+ $self->sendCmd( $cmd );
+ }
+}
+
+sub autoStop
+{
+ my $self = shift;
+ my $autostop = shift;
+ if( $autostop )
+ {
+ Debug( "Auto Stop" );
+ usleep( $autostop );
+ my $cmd = "ptzStopRun";
+ $self->sendCmd( $cmd );
+ }
+}
+
+sub moveConUp
+{
+ my $self = shift;
+ my $params = shift;
+ my $tiltspeed = $self->getParam( $params, 'tiltspeed' );
+ # speed inverter 4-->0 , 3-->1 , 2-->2 , 1-->3 , 0-->4
+ $tiltspeed = abs($tiltspeed - 4);
+ # Normalisation en cas de valeur erronée dans la base de données
+ if ( $tiltspeed > 4 ) {
+ $tiltspeed = 4;
+ }
+ if ( $tiltspeed < 0 ) {
+ $tiltspeed = 0;
+ }
+ Debug( "Move Up" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DMove Up $tiltspeed";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setPTZSpeed%26speed%3D$tiltspeed";
+ $self->sendCmd( $cmd );
+ $cmd = "ptzMoveUp";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+sub moveConDown
+{
+ my $self = shift;
+ my $params = shift;
+ my $tiltspeed = $self->getParam( $params, 'tiltspeed' );
+ # speed inverter 4-->0 , 3-->1 , 2-->2 , 1-->3 , 0-->4
+ $tiltspeed = abs($tiltspeed - 4);
+ # Normalization
+ if ( $tiltspeed > 4 ) {
+ $tiltspeed = 4;
+ }
+ if ( $tiltspeed < 0 ) {
+ $tiltspeed = 0;
+ }
+ Debug( "Move Down" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DMove Down $tiltspeed";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setPTZSpeed%26speed%3D$tiltspeed";
+ $self->sendCmd( $cmd );
+ $cmd = "ptzMoveDown";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+sub moveConLeft
+{
+ my $self = shift;
+ my $params = shift;
+ my $panspeed = $self->getParam( $params, 'panspeed' );
+ # Normalisation en cas de valeur erronée dans la base de données
+ if ( $panspeed > 4 ) {
+ $panspeed = 4;
+ }
+ if ( $panspeed < 0 ) {
+ $panspeed = 0;
+ }
+ Debug( "Move Left" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DMove Left $panspeed";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setPTZSpeed%26speed%3D$panspeed";
+ $self->sendCmd( $cmd );
+ $cmd = "ptzMoveLeft";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+
+sub moveConRight
+{
+ my $self = shift;
+ my $params = shift;
+ my $panspeed = $self->getParam( $params, 'panspeed' );
+ # speed inverter 4-->0 , 3-->1 , 2-->2 , 1-->3 , 0-->4
+ $panspeed = abs($panspeed - 4);
+ # Normalisation en cas de valeur erronée dans la base de données
+ if ( $panspeed > 4 ) {
+ $panspeed = 4;
+ }
+ if ( $panspeed < 0 ) {
+ $panspeed = 0;
+ }
+ Debug( "Move Right" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DMove Right $panspeed";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setPTZSpeed%26speed%3D$panspeed";
+ $self->sendCmd( $cmd );
+ $cmd = "ptzMoveRight";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+sub moveConUpLeft
+{
+ my $self = shift;
+ my $params = shift;
+ my $tiltspeed = $self->getParam( $params, 'tiltspeed' );
+ # speed inverter 4-->0 , 3-->1 , 2-->2 , 1-->3 , 0-->4
+ $tiltspeed = abs($tiltspeed - 4);
+ # Normalisation en cas de valeur erronée dans la base de données
+ if ( $tiltspeed > 4 ) {
+ $tiltspeed = 4;
+ }
+ if ( $tiltspeed < 0 ) {
+ $tiltspeed = 0;
+ }
+ Debug( "Move Con Up Left" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DMove Up Left $tiltspeed";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setPTZSpeed%26speed%3D$tiltspeed";
+ $self->sendCmd( $cmd );
+ $cmd = "ptzMoveTopLeft";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+sub moveConUpRight
+{
+ my $self = shift;
+ my $params = shift;
+ my $tiltspeed = $self->getParam( $params, 'tiltspeed' );
+ # speed inverter 4-->0 , 3-->1 , 2-->2 , 1-->3 , 0-->4
+ $tiltspeed = abs($tiltspeed - 4);
+ # Normalisation en cas de valeur erronée dans la base de données
+ if ( $tiltspeed > 4 ) {
+ $tiltspeed = 4;
+ }
+ if ( $tiltspeed < 0 ) {
+ $tiltspeed = 0;
+ }
+ Debug( "Move Con Up Right" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DMove Up Right $tiltspeed";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setPTZSpeed%26speed%3D$tiltspeed";
+ $self->sendCmd( $cmd );
+ $cmd = "ptzMoveTopRight";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+sub moveConDownLeft
+{
+ my $self = shift;
+ my $params = shift;
+ my $tiltspeed = $self->getParam( $params, 'tiltspeed' );
+ # speed inverter 4-->0 , 3-->1 , 2-->2 , 1-->3 , 0-->4
+ $tiltspeed = abs($tiltspeed - 4);
+ # Normalisation en cas de valeur erronée dans la base de données
+ if ( $tiltspeed > 4 ) {
+ $tiltspeed = 4;
+ }
+ if ( $tiltspeed < 0 ) {
+ $tiltspeed = 0;
+ }
+ Debug( "Move Con Down Left" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DMove Down Left $tiltspeed";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setPTZSpeed%26speed%3D$tiltspeed";
+ $self->sendCmd( $cmd );
+ $cmd = "ptzMoveBottomLeft";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+sub moveConDownRight
+{
+ my $self = shift;
+ my $params = shift;
+ my $tiltspeed = $self->getParam( $params, 'tiltspeed' );
+ # speed inverter 4-->0 , 3-->1 , 2-->2 , 1-->3 , 0-->4
+ $tiltspeed = abs($tiltspeed - 4);
+ # Normalisation en cas de valeur erronée dans la base de données
+ if ( $tiltspeed > 4 ) {
+ $tiltspeed = 4;
+ }
+ if ( $tiltspeed < 0 ) {
+ $tiltspeed = 0;
+ }
+ Debug( "Move Con Down Right" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DMove Down Right $tiltspeed";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setPTZSpeed%26speed%3D$tiltspeed";
+ $self->sendCmd( $cmd );
+ $cmd = "ptzMoveBottomRight";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+sub zoomConTele
+{
+ my $self = shift;
+ Debug( "Zoom-Tele=MANU IR LED ON" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DManual IR LED Switch ON";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setInfraLedConfig%26mode%3D1";
+ $self->sendCmd( $cmd );
+ $cmd = "openInfraLed";
+ $self->sendCmd( $cmd );
+}
+
+sub zoomConWide
+{
+ my $self = shift;
+ Debug( "Zoom-Wide=MANU IR LED OFF" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DManual IR LED Switch OFF";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setInfraLedConfig%26mode%3D1";
+ $self->sendCmd( $cmd );
+ $cmd = "closeInfraLed";
+ $self->sendCmd( $cmd );
+}
+
+sub wake
+{
+ my $self = shift;
+ Debug( "Wake=AUTO IR LED" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DAuto IR LED Mode";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setInfraLedConfig%26mode%3D0";
+ $self->sendCmd( $cmd );
+}
+
+sub focusConNear
+{
+ my $self = shift;
+ my $params = shift;
+ my $speed = $self->getParam( $params, 'speed' );
+ # Normalisation en cas de valeur erronée dans la base de données
+ if ( $speed > 100 ) {
+ $speed = 100;
+ }
+ if ( $speed < 0 ) {
+ $speed = 0;
+ }
+ Debug( "Focus Near=Sharpness" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DSharpness $speed";
+ $self->sendCmd( $cmd );
+ $cmd = "setOSDSetting%26isEnableDevName%3D1";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setSharpness%26sharpness%3D$speed";
+ $self->sendCmd( $cmd );
+ # La variable speed ne fonctionne pas en paramètre du focus, alors je l'utilise pour définir la durée de la commande
+ # le résulat est identique
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+sub focusConFar
+{
+ my $self = shift;
+ my $params = shift;
+ my $speed = $self->getParam( $params, 'speed' );
+ # Normalisation en cas de valeur erronée dans la base de données
+ if ( $speed > 100 ) {
+ $speed = 100;
+ }
+ if ( $speed < 0 ) {
+ $speed = 0;
+ }
+ Debug( "Focus Far" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DSharpness $speed";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setSharpness%26sharpness%3D$speed";
+ $self->sendCmd( $cmd );
+ # La variable speed ne fonctionne pas en paramètre du focus alors je l'utilise pour définir la durée de la commande
+ # le résulat est identique
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+sub focusAuto
+{
+ my $self = shift;
+ Debug( "Focus Auto=Reset Sharpness" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DReset Sharpness";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setSharpness%26sharpness%3D10";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+sub focusMan
+{
+ my $self = shift;
+ Debug( "Focus Manu=Reset Sharpness" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DFOSCAM FI9821W Script V1.0 By Christophe_y2k";
+ $self->sendCmd( $cmd );
+ }
+}
+
+sub whiteConIn
+{
+ my $self = shift;
+ my $params = shift;
+ my $speed = $self->getParam( $params, 'speed' );
+ # Normalisation en cas de valeur erronée dans la base de données
+ if ( $speed > 100 ) {
+ $speed = 100;
+ }
+ if ( $speed < 0 ) {
+ $speed = 0;
+ }
+ Debug( "White ConIn=brightness" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DBrightness $speed";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setBrightness%26brightness%3D$speed";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+sub whiteConOut
+{
+ my $self = shift;
+ my $params = shift;
+ my $speed = $self->getParam( $params, 'speed' );
+ # Normalisation en cas de valeur erronée dans la base de données
+ if ( $speed > 100 ) {
+ $speed = 100;
+ }
+ if ( $speed < 0 ) {
+ $speed = 0;
+ }
+ Debug( "White ConOut=Contrast" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DContrast $speed";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setContrast%26constrast%3D$speed";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+sub whiteAuto
+{
+ my $self = shift;
+ Debug( "White Auto=Brightness Reset" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DBrightness Reset";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setBrightness%26brightness%3D50";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+sub whiteMan
+{
+ my $self = shift;
+ Debug( "White Manuel=Contrast Reset" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DContrast Reset";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setContrast%26constrast%3D44";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+sub irisConOpen
+{
+ my $self = shift;
+ my $params = shift;
+ my $speed = $self->getParam( $params, 'speed' );
+ # Normalisation en cas de valeur erronée dans la base de données
+ if ( $speed > 100 ) {
+ $speed = 100;
+ }
+ if ( $speed < 0 ) {
+ $speed = 0;
+ }
+ Debug( "Iris ConOpen=Saturation" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DSaturation $speed";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setSaturation%26saturation%3D$speed";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+sub irisConClose
+{
+ my $self = shift;
+ my $params = shift;
+ my $speed = $self->getParam( $params, 'speed' );
+ # Normalisation en cas de valeur erronée dans la base de données
+ if ( $speed > 100 ) {
+ $speed = 100;
+ }
+ if ( $speed < 0 ) {
+ $speed = 0;
+ }
+ Debug( "Iris ConClose=Hue" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DHue $speed";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setHue%26hue%3D$speed";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+sub irisAuto
+{
+ my $self = shift;
+ Debug( "Iris Auto=Saturation Reset" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DSaturation Reset";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setSaturation%26saturation%3D30";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+sub irisMan
+{
+ my $self = shift;
+ Debug( "Iris Manuel=Hue Reset" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DHue Reset";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setHue%26hue%3D6";
+ $self->sendCmd( $cmd );
+ $self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
+}
+
+sub presetSet
+{
+ my $self = shift;
+ my $params = shift;
+ my $preset = $self->getParam( $params, 'preset' );
+ if ( ( $preset >= 1 ) && ( $preset <= 16 ) ) {
+ Debug( "Clear Preset $preset" );
+ my $cmd = "ptzDeletePresetPoint%26name%3D$preset";
+ $self->sendCmd( $cmd );
+ Debug( "Set Preset $preset" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DSet Preset $preset";
+ $self->sendCmd( $cmd );
+ }
+ $cmd = "ptzAddPresetPoint%26name%3D$preset";
+ $self->sendCmd( $cmd );
+ }
+}
+
+sub presetGoto
+{
+ my $self = shift;
+ my $params = shift;
+ my $preset = $self->getParam( $params, 'preset' );
+ if ( ( $preset >= 1 ) && ( $preset <= 16 ) ) {
+ Debug( "Goto Preset $preset" );
+ if ( $osd eq "on" )
+ {
+ my $cmd = "setDevName%26devName%3DGoto Preset $preset";
+ $self->sendCmd( $cmd );
+ }
+ my $cmd = "setPTZSpeed%26speed%3D0";
+ $self->sendCmd( $cmd );
+ $cmd = "ptzGotoPresetPoint%26name%3D$preset";
+ $self->sendCmd( $cmd );
+ }
+}
+
+1;
+__END__
+# Below is stub documentation for your module. You'd better edit it!
+
+=head1 FI9831W
+
+ZoneMinder::Database - Perl extension for FOSCAM FI9831W
+
+=head1 SYNOPSIS
+
+Control script for Foscam HD cameras. Tested on 9831W but
+should work on others too.
+
+=head1 DESCRIPTION
+
+Control script for Foscam HD cameras. Tested on 9831W but
+should work on others too.
+You need to set "usr=xxx&pwd=yyy" in the ControlDevice field
+of the control tab for that monitor.
+Auto TimeOut should be 1. Don't set it to less - processes
+start crashing :)
+
+=head2 EXPORT
+
+None by default.
+
+
+
+=head1 SEE ALSO
+
+=head1 AUTHOR
+
+Philip Coombes, Ephilip.coombes@zoneminder.comE
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2001-2008 Philip Coombes
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself, either Perl version 5.8.3 or,
+at your option, any later version of Perl 5 you may have available.
+
+
+=cut
+
diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/SkyIPCam7xx.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/SkyIPCam7xx.pm
index fd97c5a49..f3fc94754 100644
--- a/scripts/ZoneMinder/lib/ZoneMinder/Control/SkyIPCam7xx.pm
+++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/SkyIPCam7xx.pm
@@ -107,7 +107,14 @@ sub sendCmd
printMsg( $cmd, "Tx" );
- my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."$cmd" );
+ my $url;
+ if ( $self->{Monitor}->{ControlAddress} =~ /^http/ ) {
+ $url = $self->{Monitor}->{ControlAddress}.$cmd;
+ } else {
+ $url = 'http://'.$self->{Monitor}->{ControlAddress}.$cmd;
+ } # en dif
+ my $req = HTTP::Request->new( GET=>$url );
+
my $res = $self->{ua}->request($req);
if ( $res->is_success )
diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Serial.pm b/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Serial.pm
index a3709b5bb..ddfc53436 100644
--- a/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Serial.pm
+++ b/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Serial.pm
@@ -58,6 +58,12 @@ sub open
{
my $self = shift;
my $device = new Device::SerialPort( $self->{path} );
+ if ( ! $device )
+ {
+ Error( "Unable to open $$self{path}: $!" );
+ $self->{state} = 'closed';
+ return;
+ }
$device->baudrate(9600);
$device->databits(8);
$device->parity('none');
diff --git a/scripts/zmcontrol.pl.in b/scripts/zmcontrol.pl.in
index ee078f9a0..aba1d1c36 100644
--- a/scripts/zmcontrol.pl.in
+++ b/scripts/zmcontrol.pl.in
@@ -156,7 +156,7 @@ if ( !$server_up )
while (!connect( CLIENT, $saddr ))
{
$attempts++;
- Fatal( "Can't connect: $!" ) if ($attempts > MAX_CONNECT_DELAY);
+ Fatal( "Can't connect: $! after $attempts attempts to $sock_file" ) if ($attempts > MAX_CONNECT_DELAY);
sleep(1);
}
}
diff --git a/scripts/zmdc.pl.in b/scripts/zmdc.pl.in
index 981f7364e..5e2fb47c7 100644
--- a/scripts/zmdc.pl.in
+++ b/scripts/zmdc.pl.in
@@ -414,8 +414,7 @@ sub start
my $daemon = shift;
my @args = @_;
- my $command = $daemon;
- $command .= ' '.join( ' ', ( @args ) ) if ( @args );
+ my $command = join(' ', $daemon, @args );
my $process = $cmd_hash{$command};
if ( !$process )
@@ -506,22 +505,13 @@ sub start
}
}
-sub _stop
-{
- my $final = shift;
- my $daemon = shift;
- my @args = @_;
+# Sends the stop signal, without waiting around to see if the process died.
+sub send_stop {
+ my ( $final, $process ) = @_;
+
+ my $command = $process->{command};
+ if ( $process->{pending} ) {
- my $command = $daemon;
- $command .= ' '.join( ' ', ( @args ) ) if ( @args );
- my $process = $cmd_hash{$command};
- if ( !$process )
- {
- dPrint( ZoneMinder::Logger::WARNING, "Can't find process with command of '$command'\n" );
- return();
- }
- elsif ( $process->{pending} )
- {
delete( $cmd_hash{$command} );
dPrint( ZoneMinder::Logger::INFO, "Command '$command' removed from pending list at "
.strftime( '%y/%m/%d %H:%M:%S', localtime() )
@@ -530,37 +520,63 @@ sub _stop
return();
}
- my $cpid = $process->{pid};
- if ( !$pid_hash{$cpid} )
+ my $pid = $process->{pid};
+ if ( !$pid_hash{$pid} )
{
- dPrint( ZoneMinder::Logger::ERROR, "No process with command of '$command' is running\n" );
+ dPrint( ZoneMinder::Logger::ERROR, "No process with command of '$command' pid $pid is running\n" );
return();
}
- dPrint( ZoneMinder::Logger::INFO, "'$daemon ".join( ' ', @args )
- ."' stopping at "
+ dPrint( ZoneMinder::Logger::INFO, "'$command' sending stop to pid $pid at "
.strftime( '%y/%m/%d %H:%M:%S', localtime() )
."\n"
);
$process->{keepalive} = !$final;
- kill( 'TERM', $cpid );
- delete( $cmd_hash{$command} );
+ kill( 'TERM', $pid );
+ return $pid;
+} # end sub send_stop
+sub kill_until_dead {
+ my ( $process ) = @_;
# Now check it has actually gone away, if not kill -9 it
my $count = 0;
- while( $cpid && kill( 0, $cpid ) )
+ while( $process and $$process{pid} and kill( 0, $$process{pid} ) )
{
if ( $count++ > 5 )
{
- kill( 'KILL', $cpid );
+ dPrint( ZoneMinder::Logger::WARNING, "'$$process{command}' has not stopped at "
+ .strftime( '%y/%m/%d %H:%M:%S', localtime() )
+ .". Sending KILL to pid $$process{pid}\n"
+ );
+ kill( 'KILL', $$process{pid} );
}
+
sleep( 1 );
}
}
+sub _stop {
+ my ($final, $process ) = @_;
+
+ my $pid = send_stop( $final, $process );
+ return if ! $pid;
+ delete( $cmd_hash{$$process{command}} );
+
+ kill_until_dead( $process );
+}
+
sub stop
{
- _stop( 1, @_ );
+ my ( $daemon, @args ) = @_;
+ my $command = join(' ', $daemon, @args );
+ my $process = $cmd_hash{$command};
+ if ( !$process )
+ {
+ dPrint( ZoneMinder::Logger::WARNING, "Can't find process with command of '$command'\n" );
+ return();
+ }
+
+ _stop( 1, $process );
}
sub restart
@@ -578,7 +594,7 @@ sub restart
my $cpid = $process->{pid};
if ( defined($pid_hash{$cpid}) )
{
- _stop( 0, $daemon, @args );
+ _stop( 0, $process );
return;
}
}
@@ -639,7 +655,7 @@ sub reaper
my $exit_signal = $status&0xfe;
my $core_dumped = $status&0x01;
- my $out_str = "'$process->{daemon} ".join( ' ', @{$process->{args}} )."' ";
+ my $out_str = "'$process->{command}' ";
if ( $exit_signal )
{
if ( $exit_signal == 15 || $exit_signal == 14 ) # TERM or ALRM
@@ -678,17 +694,16 @@ sub reaper
if ( $process->{keepalive} )
{
+ # Schedule for immediate restart
+ $cmd_hash{$process->{command}} = $process;
if ( !$process->{delay} || ($process->{runtime} > $Config{ZM_MAX_RESTART_DELAY} ) )
{
#start( $process->{daemon}, @{$process->{args}} );
- # Schedule for immediate restart
- $cmd_hash{$process->{command}} = $process;
$process->{pending} = $process->{stopped};
$process->{delay} = 5;
}
else
{
- $cmd_hash{$process->{command}} = $process;
$process->{pending} = $process->{stopped}+$process->{delay};
$process->{delay} *= 2;
# Limit the start delay to 15 minutes max
@@ -718,9 +733,20 @@ sub restartPending
sub shutdownAll
{
- foreach my $process ( values( %pid_hash ) )
- {
- stop( $process->{daemon}, @{$process->{args}} );
+ foreach my $pid ( keys %pid_hash ) {
+ # This is a quick fix because a SIGCHLD can happen and alter pid_hash while we are in here.
+ next if ! $pid_hash{$pid};
+ send_stop( 1, $pid_hash{$pid} );
+ }
+ foreach my $pid ( keys %pid_hash ) {
+ # This is a quick fix because a SIGCHLD can happen and alter pid_hash while we are in here.
+ next if ! $pid_hash{$pid};
+
+ my $process = $pid_hash{$pid};
+
+ kill_until_dead( $process );
+ delete( $cmd_hash{$$process{command}} );
+ delete( $pid_hash{$pid} );
}
killAll( 5 );
dPrint( ZoneMinder::Logger::INFO, "Server shutdown at "
@@ -830,21 +856,20 @@ sub killAll
{
my $delay = shift;
sleep( $delay );
- my $killall;
- if ( '@HOST_OS@' eq 'BSD' )
- {
- $killall = 'killall -';
- } elsif ( '@HOST_OS@' eq 'solaris' ) {
- $killall = 'pkill -';
- } else {
- $killall = 'killall -q -s ';
- }
+ my $killall;
+ if ( '@HOST_OS@' eq 'BSD' )
+ {
+ $killall = 'killall -';
+ } elsif ( '@HOST_OS@' eq 'solaris' ) {
+ $killall = 'pkill -';
+ } else {
+ $killall = 'killall -q -s ';
+ }
foreach my $daemon ( @daemons )
{
-
- my $cmd = $killall ."TERM $daemon";
- Debug( $cmd );
- qx( $cmd );
+ my $cmd = $killall ."TERM $daemon";
+ Debug( $cmd );
+ qx( $cmd );
}
sleep( $delay );
foreach my $daemon ( @daemons )
diff --git a/scripts/zmpkg.pl.in b/scripts/zmpkg.pl.in
index 362f4f6a6..46ea825c4 100644
--- a/scripts/zmpkg.pl.in
+++ b/scripts/zmpkg.pl.in
@@ -55,6 +55,7 @@ use autouse 'Pod::Usage'=>qw(pod2usage);
$ENV{PATH} = '/bin:/usr/bin';
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
+my $store_state=""; # PP - will remember state name passed
logInit();
@@ -90,6 +91,7 @@ if ( !$command || $command !~ /^(?:start|stop|restart|status|logrot|version)$/ )
{ Id=>$id, Function=>$function, Enabled=>$enabled }
);
}
+ $store_state=$command; # PP - Remember the name that was passed to search in DB
$command = 'state';
}
else
@@ -149,6 +151,18 @@ if ( $command eq "state" )
}
}
$sth->finish();
+ #PP - lets go ahead and modify States DB
+
+
+ Debug ("Marking $store_state as Enabled");
+ # PP - Zero out other states being active
+ resetStates();
+ # PP - Now mark a specific state as active
+ $sql = "update States set IsActive = '1' where Name = ?";
+ $sth = $dbh->prepare_cached( $sql )
+ or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
+ $res = $sth->execute( $store_state )
+ or Fatal( "Can't execute: ".$sth->errstr() );
$command = "restart";
}
@@ -159,6 +173,9 @@ if ( $command =~ /^(start|stop|restart)$/ )
# We have to detaint to keep perl from complaining
$command = $1;
+ # PP - if we are not switching to a custom state, zero out all isActive
+ resetStates() if (!$store_state);
+
if ( systemdRunning() && !calledBysystem() ) {
qx(@BINDIR@/zmsystemctl.pl $command);
$command = "";
@@ -290,6 +307,19 @@ if ( $command eq "logrot" )
exit( $retval );
+# PP - when the system is restarted/started/stopped, it will
+# not be in a custom state, so lets keep the DB consistent
+sub resetStates
+{
+ $dbh = zmDbConnect() if ! $dbh;
+ my $sql = "update States set IsActive = '0'";
+ my $sth = $dbh->prepare_cached( $sql )
+ or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
+ my $res = $sth->execute()
+ or Fatal( "Can't execute: ".$sth->errstr() );
+
+}
+
sub systemdRunning
{
my $result = 0;
diff --git a/scripts/zmsystemctl.pl.in b/scripts/zmsystemctl.pl.in
index c825d891b..75acae242 100644
--- a/scripts/zmsystemctl.pl.in
+++ b/scripts/zmsystemctl.pl.in
@@ -31,10 +31,15 @@ zmsystemctl.pl - ZoneMinder systemctl wrapper
=head1 DESCRIPTION
-This is a wrapper script that allows zoneminder to start and stop itself
-in a manner that keeps it in-sync with systemd. This script is intended
-to be called internally by zoneminder and may not give the desired results
-if run from the command line.
+This script allows an unpriveledged user to start, stop, or restart the
+zoneminder service on a system running systemd. It does this by redirecting
+commands through pkexec, which checks the available polkit policy files. The
+default policy file grants the system web account user permission. This can be
+changed or expanded by modifying the policy file. See man polkit for details.
+
+=head1 SEE ALSO
+
+polkit(8), pkexec(1)
=cut
use warnings;
diff --git a/scripts/zmtrigger.pl.in b/scripts/zmtrigger.pl.in
index e43b00bbb..bf7c4ad13 100644
--- a/scripts/zmtrigger.pl.in
+++ b/scripts/zmtrigger.pl.in
@@ -28,7 +28,82 @@ zmtrigger.pl - ZoneMinder External Trigger Script
=head1 DESCRIPTION
This script is used to trigger and cancel alarms from external connections
-using an arbitrary text based format
+using an arbitrary text based format.
+
+This script offers generic solution to external triggering of alarms. It
+can handle external connections via either internet socket, unix socket or
+file/device interfaces. You can either use it 'as is' if you can interface
+with the existing format, or override connections and channels to customise
+it to your needs.
+
+If enabled by the OPT_TRIGGERS option, Zoneminder service start
+zmtrigger.pl which listens for control messages on TCP port 6802.
+
+=head1 TRIGGER MESSAGE FORMAT
+
+B|B|B|B|B|B
+
+=over 4
+
+=item B
+
+ is the id number or name of the ZM monitor.
+
+=item B
+
+ Valid actions are 'on', 'off', 'cancel' or 'show' where
+ 'on' forces an alarm condition on;
+ 'off' forces an alarm condition off;
+ 'cancel' negates the previous 'on' or 'off'.
+
+ The 'show' action merely updates some auxiliary text which can optionally
+ be displayed in the images captured by the monitor. Ordinarily you would
+ use 'on' and 'cancel', 'off' would tend to be used to suppress motion
+ based events. Additionally 'on' and 'off' can take an additional time
+ offset, e.g. on+20 which automatically 'cancel's the previous action
+ after that number of seconds.
+
+=item B
+
+ is the score given to the alarm, usually to indicate it's
+ importance. For 'on' triggers it should be non-zero, otherwise it should
+ be zero.
+
+=item B
+
+ is a 32 char max string indicating the reason for, or source of
+ the alarm e.g. 'Relay 1 open'. This is saved in the 'Cause' field of the
+ event. Ignored for 'off' or 'cancel' messages.
+
+=item B
+
+ is a 256 char max additional info field, which is saved in the
+ 'Description' field of an event. Ignored for 'off' or 'cancel' messages.
+
+=item B
+
+ is up to 32 characters of text that can be displayed in the
+ timestamp that is added to images. The 'show' action is designed to
+ update this text without affecting alarms but the text is updated, if
+ present, for any of the actions. This is designed to allow external input
+ to appear on the images captured, for instance temperature or personnel
+ identity etc.
+
+=back
+
+Note that multiple messages can be sent at once and should be LF or CRLF
+delimited. This script is not necessarily intended to be a solution in
+itself, but is intended to be used as 'glue' to help ZoneMinder interface
+with other systems. It will almost certainly require some customisation
+before you can make any use of it. If all you want to do is generate alarms
+from external sources then using the ZoneMinder::SharedMem perl module is
+likely to be easier.
+
+=head1 EXAMPLES
+
+ 3|on+10|1|motion|text|showtext
+
+Triggers "alarm" on camera #3 for 10 seconds with score=1, cause="motion".
=cut
use strict;
@@ -446,7 +521,7 @@ sub handleMessage
Debug( "Added timed event '$action_text', expires at $action_time (+$delay secs)\n" );
}
}
- elsif ( $action =~ /^(on|off)(?:\+(\d+))?$/ )
+ elsif ( $action =~ /^(on|off)(?:[ \+](\d+))?$/ )
{
next if ( !$monitor->{Enabled} );
diff --git a/scripts/zmwatch.pl.in b/scripts/zmwatch.pl.in
index 7d5193439..01d15e815 100644
--- a/scripts/zmwatch.pl.in
+++ b/scripts/zmwatch.pl.in
@@ -85,17 +85,67 @@ while( 1 )
or Fatal( "Can't execute: ".$sth->errstr() );
while( my $monitor = $sth->fetchrow_hashref() )
{
- if ( $monitor->{Function} ne 'None' )
+ next if $monitor->{Function} eq 'None';
+ my $restart = 0;
+ if ( zmMemVerify( $monitor )
+ && zmMemRead( $monitor, "shared_data:valid" )
+ )
{
- my $restart = 0;
- if ( zmMemVerify( $monitor )
- && zmMemRead( $monitor, "shared_data:valid" )
- )
+ # Check we have got an image recently
+ my $image_time = zmGetLastWriteTime( $monitor );
+ next if ( !defined($image_time) ); # Can't read from shared data
+ next if ( !$image_time ); # We can't get the last capture time so can't be sure it's died.
+
+ my $max_image_delay = ( $monitor->{MaxFPS}
+ &&($monitor->{MaxFPS}>0)
+ &&($monitor->{MaxFPS}<1)
+ ) ? (3/$monitor->{MaxFPS})
+ : $Config{ZM_WATCH_MAX_DELAY}
+ ;
+ my $image_delay = $now-$image_time;
+ Debug( "Monitor $monitor->{Id} last captured $image_delay seconds ago, max is $max_image_delay\n" );
+ if ( $image_delay > $max_image_delay )
{
- # Check we have got an image recently
- my $image_time = zmGetLastWriteTime( $monitor );
- next if ( !defined($image_time) ); # Can't read from shared data
- next if ( !$image_time ); # We can't get the last capture time so can't be sure it's died.
+ Info( "Restarting capture daemon for "
+ .$monitor->{Name}.", time since last capture $image_delay seconds ($now-$image_time)\n"
+ );
+ $restart = 1;
+ }
+ }
+ else
+ {
+ Info( "Restarting capture daemon for ".$monitor->{Name}.", shared data not valid\n" );
+ $restart = 1;
+ }
+
+ if ( $restart )
+ {
+ my $command;
+ if ( $monitor->{Type} eq 'Local' )
+ {
+ $command = "zmdc.pl restart zmc -d $monitor->{Device}";
+ }
+ else
+ {
+ $command = "zmdc.pl restart zmc -m $monitor->{Id}";
+ }
+ runCommand( $command );
+ }
+ elsif ( $monitor->{Function} ne 'Monitor' )
+ {
+ # Now check analysis daemon
+ $restart = 0;
+ # Check we have got an image recently
+ my $image_time = zmGetLastReadTime( $monitor );
+ if ( !defined($image_time) ) {
+ # Can't read from shared data
+ $restart = 1;
+ Error( "Error reading shared data for $$monitor{id} $$monitor{Name}\n");
+ } elsif ( !$image_time ) {
+ # We can't get the last capture time so can't be sure it's died.
+ $restart = 1;
+ Error( "Error getting last capture time for $$monitor{id} $$monitor{Name}\n");
+ } else {
my $max_image_delay = ( $monitor->{MaxFPS}
&&($monitor->{MaxFPS}>0)
@@ -104,68 +154,27 @@ while( 1 )
: $Config{ZM_WATCH_MAX_DELAY}
;
my $image_delay = $now-$image_time;
- Debug( "Monitor $monitor->{Id} last captured $image_delay seconds ago, max is $max_image_delay\n" );
+ Debug( "Monitor $monitor->{Id} last analysed $image_delay seconds ago, max is $max_image_delay\n" );
if ( $image_delay > $max_image_delay )
{
- Info( "Restarting capture daemon for "
- .$monitor->{Name}.", time since last capture $image_delay seconds ($now-$image_time)\n"
+ Info( "Analysis daemon for $$monitor{id} $$monitor{Name} needs restarting,"
+ ." time since last analysis $image_delay seconds ($now-$image_time)\n"
);
$restart = 1;
}
}
- else
- {
- #Info( "Restarting capture daemon for ".$monitor->{Name}.", shared data not valid\n" );
- #$restart = 1;
- }
if ( $restart )
{
- my $command;
- if ( $monitor->{Type} eq 'Local' )
- {
- $command = "zmdc.pl restart zmc -d $monitor->{Device}";
- }
- else
- {
- $command = "zmdc.pl restart zmc -m $monitor->{Id}";
- }
+ Info( "Restarting analysis daemon for $$monitor{id} $$monitor{Name}\n");
+ my $command = "zmdc.pl restart zma -m ".$monitor->{Id};
runCommand( $command );
- }
- elsif ( $monitor->{Function} ne 'Monitor' )
- {
- if ( zmMemVerify( $monitor )
- && zmMemRead( $monitor, "shared_data:valid" )
- )
- {
- # Check we have got an image recently
- my $image_time = zmGetLastReadTime( $monitor );
- next if ( !defined($image_time) ); # Can't read from shared data
- next if ( !$image_time ); # We can't get the last capture time so can't be sure it's died.
-
- my $max_image_delay = ( $monitor->{MaxFPS}
- &&($monitor->{MaxFPS}>0)
- &&($monitor->{MaxFPS}<1)
- ) ? (3/$monitor->{MaxFPS})
- : $Config{ZM_WATCH_MAX_DELAY}
- ;
- my $image_delay = $now-$image_time;
- Debug( "Monitor $monitor->{Id} last analysed $image_delay seconds ago, max is $max_image_delay\n" );
- if ( $image_delay > $max_image_delay )
- {
- Info( "Restarting analysis daemon for "
- .$monitor->{Name}.", time since last analysis $image_delay seconds ($now-$image_time)\n"
- );
- my $command = "zmdc.pl restart zma -m ".$monitor->{Id};
- runCommand( $command );
- }
- }
- }
- }
+ } # end if restart
+ } # end if check analysis daemon
# Prevent open handles building up if we have connect to shared memory
zmMemInvalidate( $monitor );
- }
+ } # end foreach monitor
sleep( $Config{ZM_WATCH_CHECK_INTERVAL} );
-}
+} # end while (1)
Info( "Watchdog exiting\n" );
exit();
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0e8de2aa9..75b681026 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -39,6 +39,11 @@ target_link_libraries(zmf -ldl zoneminder ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS})
target_link_libraries(zms -ldl zoneminder ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS})
target_link_libraries(zmstreamer -ldl zoneminder ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS})
+# Generate man files for the binaries destined for the bin folder
+FOREACH(CBINARY zma zmc zmf zmu zmstreamer)
+ POD2MAN(${CMAKE_CURRENT_SOURCE_DIR}/${CBINARY}.cpp zoneminder-${CBINARY} 8)
+ENDFOREACH(CBINARY zma zmc zmf zmu zmstreamer)
+
install(TARGETS zmc zma zmu zmf zmstreamer RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(TARGETS zms RUNTIME DESTINATION "${ZM_CGIDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(CODE "execute_process(COMMAND ln -sf zms nph-zms WORKING_DIRECTORY \"\$ENV{DESTDIR}${ZM_CGIDIR}\")")
diff --git a/src/zm_buffer.cpp b/src/zm_buffer.cpp
index 384f45bcc..d2fb2ef72 100644
--- a/src/zm_buffer.cpp
+++ b/src/zm_buffer.cpp
@@ -18,6 +18,7 @@
*/
#include
+#include
#include "zm.h"
#include "zm_buffer.h"
@@ -67,3 +68,14 @@ unsigned int Buffer::expand( unsigned int count )
}
return( mSize );
}
+
+int Buffer::read_into( int sd, unsigned int bytes ) {
+ // Make sure there is enough space
+ this->expand(bytes);
+ int bytes_read = read( sd, mTail, bytes );
+ if ( bytes_read > 0 ) {
+ mTail += bytes_read;
+ mSize += bytes_read;
+ }
+ return bytes_read;
+}
diff --git a/src/zm_buffer.h b/src/zm_buffer.h
index 908642a2c..a0c231a3f 100644
--- a/src/zm_buffer.h
+++ b/src/zm_buffer.h
@@ -203,6 +203,7 @@ public:
{
return( (int)mSize );
}
+ int read_into( int sd, unsigned int bytes );
};
#endif // ZM_BUFFER_H
diff --git a/src/zm_db.cpp b/src/zm_db.cpp
index 4ebc2f11f..0065488f5 100644
--- a/src/zm_db.cpp
+++ b/src/zm_db.cpp
@@ -75,3 +75,38 @@ void zmDbClose()
zmDbConnected = false;
}
}
+
+MYSQL_RES * zmDbFetch( const char * query ) {
+ if ( ! zmDbConnected ) {
+ Error( "Not connected." );
+ return NULL;
+ }
+
+ if ( mysql_query( &dbconn, query ) ) {
+ Error( "Can't run query: %s", mysql_error( &dbconn ) );
+ return NULL;
+ }
+ Debug( 4, "Success running query: %s", query );
+ MYSQL_RES *result = mysql_store_result( &dbconn );
+ if ( !result ) {
+ Error( "Can't use query result: %s for query %s", mysql_error( &dbconn ), query );
+ return NULL;
+ }
+ return result;
+} // end MYSQL_RES * zmDbFetch( const char * query );
+
+MYSQL_ROW zmDBFetchOne( const char *query ) {
+ MYSQL_RES *result = zmDbFetch( query );
+ int n_rows = mysql_num_rows( result );
+ if ( n_rows != 1 ) {
+ Error( "Bogus number of lines return from query, %d returned for query %s.", n_rows, query );
+ return NULL;
+ }
+
+ MYSQL_ROW dbrow = mysql_fetch_row( result );
+ if ( ! dbrow ) {
+ Error("Error getting row from query %s. Error is %s", query, mysql_error( &dbconn ) );
+ return NULL;
+ }
+ return dbrow;
+}
diff --git a/src/zm_db.h b/src/zm_db.h
index 07fd188a2..de1a81340 100644
--- a/src/zm_db.h
+++ b/src/zm_db.h
@@ -32,6 +32,9 @@ extern int zmDbConnected;
void zmDbConnect();
void zmDbClose();
+MYSQL_RES * zmDbFetch( const char *query );
+MYSQL_ROW zmDBFetchOne( const char *query );
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/src/zm_image.cpp b/src/zm_image.cpp
index 21e190ffb..8f91ab28b 100644
--- a/src/zm_image.cpp
+++ b/src/zm_image.cpp
@@ -2596,8 +2596,8 @@ void Image::Scale( unsigned int factor )
return;
}
- unsigned int new_width = (width*factor)/ZM_SCALE_BASE;
- unsigned int new_height = (height*factor)/ZM_SCALE_BASE;
+ unsigned int new_width = (width*factor)/ZM_SCALE_BASE+1;
+ unsigned int new_height = (height*factor)/ZM_SCALE_BASE+1;
size_t scale_buffer_size = new_width * new_height * colours;
diff --git a/src/zm_logger.cpp b/src/zm_logger.cpp
index 6be21022b..4fb1ba64b 100644
--- a/src/zm_logger.cpp
+++ b/src/zm_logger.cpp
@@ -601,8 +601,7 @@ void Logger::logPrint( bool hex, const char * const file, const int line, const
if ( mysql_query( &mDbConnection, sql ) )
{
databaseLevel( NOLOG );
- Fatal( "Can't insert log entry: %s", mysql_error( &mDbConnection ) );
- exit( mysql_errno( &mDbConnection ) );
+ Error( "Can't insert log entry: %s", mysql_error( &mDbConnection ) );
}
}
if ( level <= mSyslogLevel )
diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp
index b5e5aaddf..cc32cc9b8 100644
--- a/src/zm_monitor.cpp
+++ b/src/zm_monitor.cpp
@@ -721,7 +721,7 @@ Monitor::State Monitor::GetState() const
return( (State)shared_data->state );
}
-int Monitor::GetImage( int index, int scale ) const
+int Monitor::GetImage( int index, int scale )
{
if ( index < 0 || index > image_buffer_count )
{
@@ -730,21 +730,32 @@ int Monitor::GetImage( int index, int scale ) const
if ( index != image_buffer_count )
{
- Snapshot *snap = &image_buffer[index];
- Image snap_image( *(snap->image) );
+ Image *image;
+ // If we are going to be modifying the snapshot before writing, then we need to copy it
+ if ( ( scale != ZM_SCALE_BASE ) || ( !config.timestamp_on_capture ) ) {
+ Snapshot *snap = &image_buffer[index];
+ Image *snap_image = snap->image;
- if ( scale != ZM_SCALE_BASE )
- {
- snap_image.Scale( scale );
+ alarm_image.Assign( *snap_image );
+
+
+ //write_image.Assign( *snap_image );
+
+ if ( scale != ZM_SCALE_BASE ) {
+ alarm_image.Scale( scale );
+ }
+
+ if ( !config.timestamp_on_capture ) {
+ TimestampImage( &alarm_image, snap->timestamp );
+ }
+ image = &alarm_image;
+ } else {
+ image = image_buffer[index].image;
}
static char filename[PATH_MAX];
snprintf( filename, sizeof(filename), "Monitor%d.jpg", id );
- if ( !config.timestamp_on_capture )
- {
- TimestampImage( &snap_image, snap->timestamp );
- }
- snap_image.WriteJpeg( filename );
+ image->WriteJpeg( filename );
}
else
{
@@ -1668,7 +1679,7 @@ bool Monitor::Analyse()
if ( config.create_analysis_images )
{
bool got_anal_image = false;
- Image alarm_image( *snap_image );
+ alarm_image.Assign( *snap_image );
for( int i = 0; i < n_zones; i++ )
{
if ( zones[i]->Alarmed() )
diff --git a/src/zm_monitor.h b/src/zm_monitor.h
index a982ea720..699d79499 100644
--- a/src/zm_monitor.h
+++ b/src/zm_monitor.h
@@ -251,6 +251,9 @@ protected:
double fps;
+ Image alarm_image; // Used in creating analysis images, will be initialized in Analysis
+ Image write_image; // Used when creating snapshot images
+
Purpose purpose; // What this monitor has been created to do
int event_count;
int image_count;
@@ -357,7 +360,7 @@ public:
State GetState() const;
- int GetImage( int index=-1, int scale=100 ) const;
+ int GetImage( int index=-1, int scale=100 );
struct timeval GetTimestamp( int index=-1 ) const;
int GetCaptureDelay() const { return( capture_delay ); }
int GetAlarmCaptureDelay() const { return( alarm_capture_delay ); }
@@ -480,9 +483,9 @@ public:
{
ttl = p_ttl;
}
- void setStreamStart( int monitor_id )
+ bool setStreamStart( int monitor_id )
{
- loadMonitor( monitor_id );
+ return loadMonitor( monitor_id );
}
void runStream();
};
diff --git a/src/zm_remote_camera_http.cpp b/src/zm_remote_camera_http.cpp
index c6de64aed..44004564f 100644
--- a/src/zm_remote_camera_http.cpp
+++ b/src/zm_remote_camera_http.cpp
@@ -18,6 +18,7 @@
//
#include "zm_remote_camera_http.h"
+#include "zm_rtsp_auth.h"
#include "zm_mem_utils.h"
@@ -147,6 +148,12 @@ int RemoteCameraHttp::SendRequest()
return( 0 );
}
+/* Return codes are as follows:
+ * -1 means there was an error
+ * 0 means no bytes were returned but there wasn't actually an error.
+ * > 0 is the # of bytes read.
+ */
+
int RemoteCameraHttp::ReadData( Buffer &buffer, int bytes_expected )
{
fd_set rfds;
@@ -158,8 +165,9 @@ int RemoteCameraHttp::ReadData( Buffer &buffer, int bytes_expected )
int n_found = select( sd+1, &rfds, NULL, NULL, &temp_timeout );
if( n_found == 0 )
{
- Warning( "Select timed out" );
- Disconnect();
+ Debug( 4, "Select timed out timeout was %d secs %d usecs", temp_timeout.tv_sec, temp_timeout.tv_usec );
+ // Why are we disconnecting? It's just a timeout, meaning that data wasn't available.
+ //Disconnect();
return( 0 );
}
else if ( n_found < 0)
@@ -184,20 +192,27 @@ int RemoteCameraHttp::ReadData( Buffer &buffer, int bytes_expected )
if ( total_bytes_to_read == 0 )
{
- Debug( 3, "Socket closed" );
- Disconnect();
- return( 0 );
+ // If socket is closed locally, then select will fail, but if it is closed remotely
+ // then we have an exception on our socket.. but no data.
+ Debug( 3, "Socket closed remotely" );
+ //Disconnect(); // Disconnect is done outside of ReadData now.
+ return( -1 );
}
- }
- Debug( 3, "Expecting %d bytes", total_bytes_to_read );
+
+ // There can be lots of bytes available. I've seen 4MB or more. This will vastly inflate our buffer size unneccessarily.
+ if ( total_bytes_to_read > ZM_NETWORK_BUFSIZ ) {
+ total_bytes_to_read = ZM_NETWORK_BUFSIZ;
+ Debug(3, "Just getting 32K" );
+ } else {
+ Debug(3, "Just getting %d", total_bytes_to_read );
+ }
+ }
+ Debug( 3, "Expecting %d bytes", total_bytes_to_read );
int total_bytes_read = 0;
do
{
- static unsigned char temp_buffer[ZM_NETWORK_BUFSIZ];
- int bytes_to_read = (unsigned int)total_bytes_to_read>(unsigned int)sizeof(temp_buffer)?sizeof(temp_buffer):total_bytes_to_read;
- int bytes_read = read( sd, temp_buffer, bytes_to_read );
-
+ int bytes_read = buffer.read_into( sd, total_bytes_to_read );
if ( bytes_read < 0)
{
Error( "Read error: %s", strerror(errno) );
@@ -205,17 +220,16 @@ int RemoteCameraHttp::ReadData( Buffer &buffer, int bytes_expected )
}
else if ( bytes_read == 0)
{
- Debug( 3, "Socket closed" );
- Disconnect();
- return( 0 );
+ Debug( 2, "Socket closed" );
+ //Disconnect(); // Disconnect is done outside of ReadData now.
+ return( -1 );
}
- else if ( bytes_read < bytes_to_read )
+ else if ( bytes_read < total_bytes_to_read )
{
- Error( "Incomplete read, expected %d, got %d", bytes_to_read, bytes_read );
+ Error( "Incomplete read, expected %d, got %d", total_bytes_to_read, bytes_read );
return( -1 );
}
Debug( 3, "Read %d bytes", bytes_read );
- buffer.append( temp_buffer, bytes_read );
total_bytes_read += bytes_read;
total_bytes_to_read -= bytes_read;
}
@@ -228,6 +242,7 @@ int RemoteCameraHttp::ReadData( Buffer &buffer, int bytes_expected )
int RemoteCameraHttp::GetResponse()
{
+ int buffer_len;
#if HAVE_LIBPCRE
if ( method == REGEXP )
{
@@ -257,17 +272,12 @@ int RemoteCameraHttp::GetResponse()
static RegExpr *content_length_expr = 0;
static RegExpr *content_type_expr = 0;
- int buffer_len = ReadData( buffer );
- if ( buffer_len == 0 )
- {
- Error( "Connection dropped by remote end" );
- return( 0 );
- }
- else if ( buffer_len < 0 )
- {
- Error( "Unable to read header data" );
- return( -1 );
+ while ( ! ( buffer_len = ReadData( buffer ) ) ) {
}
+ if ( buffer_len < 0 ) {
+ Error( "Unable to read header data" );
+ return( -1 );
+ }
if ( !header_expr )
header_expr = new RegExpr( "^(.+?\r?\n\r?\n)", PCRE_DOTALL );
if ( header_expr->Match( (char*)buffer, buffer.size() ) == 2 )
@@ -435,16 +445,12 @@ int RemoteCameraHttp::GetResponse()
else
{
Debug( 3, "Unable to extract subheader from stream, retrying" );
- int buffer_len = ReadData( buffer );
- if ( buffer_len == 0 )
- {
- Error( "Connection dropped by remote end" );
- return( 0 );
- }
- else if ( buffer_len < 0 )
- {
- return( -1 );
+ while ( ! ( buffer_len = ReadData( buffer ) ) ) {
}
+ if ( buffer_len < 0 ) {
+ Error( "Unable to extract subheader data" );
+ return( -1 );
+ }
}
break;
}
@@ -479,14 +485,8 @@ int RemoteCameraHttp::GetResponse()
{
while ( (long)buffer.size() < content_length )
{
- int buffer_len = ReadData( buffer );
- if ( buffer_len == 0 )
- {
- Error( "Connection dropped by remote end" );
- return( 0 );
- }
- else if ( buffer_len < 0 )
- {
+Debug(3, "Need more data buffer %d < content length %d", buffer.size(), content_length );
+ if ( ReadData( buffer ) < 0 ) {
Error( "Unable to read content" );
return( -1 );
}
@@ -497,54 +497,26 @@ int RemoteCameraHttp::GetResponse()
{
while ( !content_length )
{
- int buffer_len = ReadData( buffer );
- if ( buffer_len == 0 )
- {
- if ( mode == MULTI_IMAGE )
- {
- Error( "Connection dropped by remote end" );
- return( 0 );
- }
- }
- else if ( buffer_len < 0 )
- {
- Error( "Unable to read content" );
- return( -1 );
+ while ( ! ( buffer_len = ReadData( buffer ) ) ) {
}
+ if ( buffer_len < 0 ) {
+ Error( "Unable to read content" );
+ return( -1 );
+ }
static RegExpr *content_expr = 0;
- if ( buffer_len )
- {
- if ( mode == MULTI_IMAGE )
- {
- if ( !content_expr )
- {
- char content_pattern[256] = "";
- snprintf( content_pattern, sizeof(content_pattern), "^(.+?)(?:\r?\n)*(?:--)?%s\r?\n", content_boundary );
- content_expr = new RegExpr( content_pattern, PCRE_DOTALL );
- }
- if ( content_expr->Match( buffer, buffer.size() ) == 2 )
- {
- content_length = content_expr->MatchLength( 1 );
- Debug( 3, "Got end of image by pattern, content-length = %d", content_length );
- }
- }
- }
- else
- {
- content_length = buffer.size();
- Debug( 3, "Got end of image by closure, content-length = %d", content_length );
- if ( mode == SINGLE_IMAGE )
- {
- if ( !content_expr )
- {
- content_expr = new RegExpr( "^(.+?)(?:\r?\n){1,2}?$", PCRE_DOTALL );
- }
- if ( content_expr->Match( buffer, buffer.size() ) == 2 )
- {
- content_length = content_expr->MatchLength( 1 );
- Debug( 3, "Trimmed end of image, new content-length = %d", content_length );
- }
- }
+ if ( mode == MULTI_IMAGE )
+ {
+ if ( !content_expr )
+ {
+ char content_pattern[256] = "";
+ snprintf( content_pattern, sizeof(content_pattern), "^(.+?)(?:\r?\n)*(?:--)?%s\r?\n", content_boundary );
+ content_expr = new RegExpr( content_pattern, PCRE_DOTALL );
+ }
+ if ( content_expr->Match( buffer, buffer.size() ) == 2 )
+ {
+ content_length = content_expr->MatchLength( 1 );
+ Debug( 3, "Got end of image by pattern, content-length = %d", content_length );
+ }
}
}
}
@@ -650,17 +622,12 @@ int RemoteCameraHttp::GetResponse()
}
case HEADERCONT :
{
- int buffer_len = ReadData( buffer );
- if ( buffer_len == 0 )
- {
- Error( "Connection dropped by remote end" );
- return( 0 );
- }
- else if ( buffer_len < 0 )
- {
- Error( "Unable to read header" );
- return( -1 );
+ while ( ! ( buffer_len = ReadData( buffer ) ) ) {
}
+ if ( buffer_len < 0 ) {
+ Error( "Unable to read header" );
+ return( -1 );
+ }
char *crlf = 0;
char *header_ptr = (char *)buffer;
@@ -1004,17 +971,12 @@ int RemoteCameraHttp::GetResponse()
else
{
Debug( 3, "Unable to extract subheader from stream, retrying" );
- int buffer_len = ReadData( buffer );
- if ( buffer_len == 0 )
- {
- Error( "Connection dropped by remote end" );
- return( 0 );
- }
- else if ( buffer_len < 0 )
- {
- Error( "Unable to read subheader" );
- return( -1 );
+ while ( ! ( buffer_len = ReadData( buffer ) ) ) {
}
+ if ( buffer_len < 0 ) {
+ Error( "Unable to read subheader" );
+ return( -1 );
+ }
state = SUBHEADERCONT;
}
break;
@@ -1060,14 +1022,7 @@ int RemoteCameraHttp::GetResponse()
while ( (long)buffer.size() < content_length )
{
//int buffer_len = ReadData( buffer, content_length-buffer.size() );
- int buffer_len = ReadData( buffer );
- if ( buffer_len == 0 )
- {
- Error( "Connection dropped by remote end" );
- return( 0 );
- }
- else if ( buffer_len < 0 )
- {
+ if ( ReadData( buffer ) < 0 ) {
Error( "Unable to read content" );
return( -1 );
}
@@ -1079,16 +1034,8 @@ int RemoteCameraHttp::GetResponse()
int content_pos = 0;
while ( !content_length )
{
- int buffer_len = ReadData( buffer );
- if ( buffer_len == 0 )
- {
- if ( mode == MULTI_IMAGE )
- {
- Error( "Connection dropped by remote end" );
- return( 0 );
- }
- }
- else if ( buffer_len < 0 )
+ buffer_len = ReadData( buffer );
+ if ( buffer_len < 0 )
{
Error( "Unable to read content" );
return( -1 );
@@ -1147,7 +1094,7 @@ int RemoteCameraHttp::GetResponse()
}
}
- Debug( 3, "Returning %d (%d) bytes of captured content", content_length, buffer.size() );
+ Debug( 3, "Returning %d bytes, buffer size: (%d) bytes of captured content", content_length, buffer.size() );
return( content_length );
}
}
@@ -1191,7 +1138,7 @@ int RemoteCameraHttp::Capture( Image &image )
}
if ( content_length < 0 )
{
- Error( "Unable to get response" );
+ Error( "Unable to get response, disconnecting" );
Disconnect();
return( -1 );
}
diff --git a/src/zm_rtsp.cpp b/src/zm_rtsp.cpp
index 02fdc4e22..556174291 100644
--- a/src/zm_rtsp.cpp
+++ b/src/zm_rtsp.cpp
@@ -663,7 +663,7 @@ int RtspThread::run()
{
now = time(NULL);
// Send a keepalive message if the server supports this feature and we are close to the timeout expiration
-Debug(5, "sendkeepalibe %d, timeout %d, now: %d last: %d since: %d", sendKeepalive, timeout, now, lastKeepalive, (now-lastKeepalive) );
+Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepalive, timeout, now, lastKeepalive, (now-lastKeepalive) );
if ( sendKeepalive && (timeout > 0) && ((now-lastKeepalive) > (timeout-5)) )
{
if ( !sendCommand( message ) )
@@ -808,7 +808,7 @@ Debug(5, "sendkeepalibe %d, timeout %d, now: %d last: %d since: %d", sendKeepali
// FIXME: Is this really necessary when using tcp ?
now = time(NULL);
// Send a keepalive message if the server supports this feature and we are close to the timeout expiration
-Debug(5, "sendkeepalibe %d, timeout %d, now: %d last: %d since: %d", sendKeepalive, timeout, now, lastKeepalive, (now-lastKeepalive) );
+Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepalive, timeout, now, lastKeepalive, (now-lastKeepalive) );
if ( sendKeepalive && (timeout > 0) && ((now-lastKeepalive) > (timeout-5)) )
{
if ( !sendCommand( message ) )
diff --git a/src/zma.cpp b/src/zma.cpp
index da8d619fe..f961bb4c8 100644
--- a/src/zma.cpp
+++ b/src/zma.cpp
@@ -17,6 +17,38 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
+/*
+
+=head1 NAME
+
+zma - The ZoneMinder Analysis daemon
+
+=head1 SYNOPSIS
+
+ zma -m
+ zma --monitor
+ zma -h
+ zma --help
+ zma -v
+ zma --version
+
+=head1 DESCRIPTION
+
+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.
+
+=head1 OPTIONS
+
+ -m, --monitor_id - ID of the monitor to analyse
+ -h, --help - Display usage information
+ -v, --version - Print the installed version of ZoneMinder
+
+=cut
+
+*/
+
#include
#include
diff --git a/src/zmc.cpp b/src/zmc.cpp
index a1c901267..814f400f3 100644
--- a/src/zmc.cpp
+++ b/src/zmc.cpp
@@ -17,6 +17,44 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
+/*
+
+=head1 NAME
+
+zmc - The ZoneMinder Capture daemon
+
+=head1 SYNOPSIS
+
+ zmc -d
+ zmc --device
+ zmc -r -H -P -p
+ zmc -f
+ zmc --file
+ zmc -m
+ zmc --monitor
+ zmc -h
+ zmc --help
+ zmc -v
+ zmc --version
+
+=head1 DESCRIPTION
+
+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.
+
+=head1 OPTIONS
+
+ -d, --device - For local cameras, device to access. e.g /dev/video0 etc
+ -r -H -P -p - For remote cameras
+ -f, --file - For local images, jpg file to access.
+ -m, --monitor_id - ID of the monitor to analyse
+ -h, --help - Display usage information
+ -v, --version - Print the installed version of ZoneMinder
+
+=cut
+
+*/
+
#include
#include
#if defined(__FreeBSD__)
@@ -211,7 +249,7 @@ int main( int argc, char *argv[] )
exit ( -1 );
}
- Info( "Starting Capture" );
+ Info( "Starting Capture version %s", ZM_VERSION );
zmSetDefaultTermHandler();
zmSetDefaultDieHandler();
diff --git a/src/zmf.cpp b/src/zmf.cpp
index 178ddf2b5..2ec592589 100644
--- a/src/zmf.cpp
+++ b/src/zmf.cpp
@@ -17,6 +17,39 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
+/*
+
+=head1 NAME
+
+zmf - The ZoneMinder Frame daemon
+
+=head1 SYNOPSIS
+
+ zmf -m
+ zmf --monitor
+ zmf -h
+ zmf --help
+ zmf -v
+ zmf --version
+
+=head1 DESCRIPTION
+
+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.
+
+=head1 OPTIONS
+
+ -m, --monitor_id - ID of the monitor to use
+ -h, --help - Display usage information
+ -v, --version - Print the installed version of ZoneMinder
+
+=cut
+
+*/
+
#include
#include
#include
diff --git a/src/zms.cpp b/src/zms.cpp
index dabe667c7..0c8cecbde 100644
--- a/src/zms.cpp
+++ b/src/zms.cpp
@@ -263,7 +263,13 @@ int main( int argc, const char *argv[] )
stream.setStreamTTL( ttl );
stream.setStreamQueue( connkey );
stream.setStreamBuffer( playback_buffer );
- stream.setStreamStart( monitor_id );
+ if ( ! stream.setStreamStart( monitor_id ) ) {
+ Error( "Unable to connect to zmc process for monitor %d", monitor_id );
+ fprintf( stderr, "Unable to connect to zmc process. Please ensure that it is running." );
+ logTerm();
+ zmDbClose();
+ return( -1 );
+ }
if ( mode == ZMS_JPEG )
{
diff --git a/src/zmstreamer.cpp b/src/zmstreamer.cpp
index 41ae138f1..cb4edd109 100644
--- a/src/zmstreamer.cpp
+++ b/src/zmstreamer.cpp
@@ -19,6 +19,52 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+/*
+
+=head1 NAME
+
+zmstreamer - eyeZM video streamer
+
+=head1 SYNOPSIS
+
+ zmstreamer -e
+ zmstreamer -o
+ zmstreamer -u
+ zmstreamer -f
+ zmstreamer -s
+ zmstreamer -b
+ zmstreamer -m
+ zmstreamer -d
+ zmstreamer -i
+ zmstreamer -?
+ zmstreamer -h
+ zmstreamer -v
+
+=head1 DESCRIPTION
+
+*DEPRECIATED* The xml skin and all files associated with the xml skin are now
+depreciated. Please use the ZoneMinder API instead.
+
+This binary works in conjunction with the XML skin to stream video to iPhones
+running the eyeZm app.
+
+=head1 OPTIONS
+
+ -e - Specify output mode: mpeg/jpg/zip/single/raw.
+ -o - Specify output format.
+ -u - Specify buffer size in ms.
+ -f - Specify maximum framerate.
+ -s - Specify scale.
+ -b - Specify bitrate.
+ -m - Specify monitor id.
+ -d - 0 = off, 1 = no streaming, 2 = with streaming.
+ -i, -?, -h - Display usage information
+ -v - Print the installed version of ZoneMinder
+
+=cut
+
+*/
+
#include
#include
diff --git a/src/zmu.cpp b/src/zmu.cpp
index f328e48dd..a382fc701 100644
--- a/src/zmu.cpp
+++ b/src/zmu.cpp
@@ -17,6 +17,75 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
+/*
+
+=head1 NAME
+
+zmc - The ZoneMinder Utility
+
+=head1 SYNOPSIS
+
+ zmu -d device_path [-v] [function] [-U -P]
+ zmu --device device_path [-v] [function] [-U -P]
+
+ zmu -m monitor_id [-v] [function] [-U -P]
+ zmu --monitor monitor_id [-v] [function] [-U -P]
+
+=head1 DESCRIPTION
+
+This binary is a handy command line interface to several useful functions. It's
+not really meant to be used by anyone except the web page (there's only limited
+'help' in it so far) but can be if necessary, especially for debugging video
+problems.
+
+=head1 OPTIONS
+
+General options:
+ -v, --verbose - Produce more verbose output
+ -l, --list - List the current status of active (or all with -v) monitors
+ -h, --help - Display usage information
+ -v, --version - Print the installed version of ZoneMinder
+
+Options for use with devices:
+ -d, --device [device_path] - Get the current video device settings for [device_path] or all devices
+ -V, --version - Set the Video 4 Linux API version to use for the query, use 1 or 2
+ -q, --query - Query the current settings for the device
+
+Options for use with monitors:
+ -m, --monitor - Specify which monitor to address, default 1 if absent
+ -q, --query - Query the current settings for the monitor
+ -s, --state - Output the current monitor state, 0 = idle, 1 = prealarm, 2 = alarm,
+ 3 = alert, 4 = tape
+ -B, --brightness [value] - Output the current brightness, set to value if given
+ -C, --contrast [value] - Output the current contrast, set to value if given
+ -H, --hue [value] - Output the current hue, set to value if given
+ -O, --colour [value] - Output the current colour, set to value if given
+ -i, --image [image_index] - Write captured image to disk as .jpg, last image captured
+ or specified ring buffer index if given.
+ -S, --scale - With --image specify any scaling (in %%) to be applied to the image
+ -t, --timestamp [image_index] - Output captured image timestamp, last image captured or specified
+ ring buffer index if given
+ -R, --read_index - Output ring buffer read index
+ -W, --write_index - Output ring buffer write index
+ -e, --event - Output last event index
+ -f, --fps - Output last Frames Per Second captured reading
+ -z, --zones - Write last captured image overlaid with zones to -Zones.jpg
+ -a, --alarm - Force alarm in monitor, this will trigger recording until cancelled with -c
+ -n, --noalarm - Force no alarms in monitor, this will prevent alarms until cancelled with -c
+ -c, --cancel - Cancel a forced alarm/noalarm in monitor, required after being enabled with -a or -n
+ -L, --reload - Signal monitor to reload settings
+ -E, --enable - Enable detection, wake monitor up
+ -D, --disable - Disable detection, put monitor to sleep
+ -u, --suspend - Suspend detection, useful to prevent bogus alarms when panning etc
+ -r, --resume - Resume detection after a suspend
+ -U, --username - When running in authenticated mode the username and
+ -P, --password - password combination of the given user
+ -A, --auth - Pass authentication hash string instead of user details
+
+=cut
+
+*/
+
#include
#include "zm.h"
diff --git a/version b/version
index 450a687b2..1bffe5fba 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-1.28.1
+1.28.100
diff --git a/web/CMakeLists.txt b/web/CMakeLists.txt
index f7bb3ec0c..b9e75a7e8 100644
--- a/web/CMakeLists.txt
+++ b/web/CMakeLists.txt
@@ -17,6 +17,7 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/includes/config.php" DESTINATION "${Z
if(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR))
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/api/app/Config/core.php" DESTINATION "${ZM_WEBDIR}/api/app/Config")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/api/app/Config/database.php" DESTINATION "${ZM_WEBDIR}/api/app/Config")
+ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/api/app/Config/bootstrap.php" DESTINATION "${ZM_WEBDIR}/api/app/Config")
endif(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR))
# Install the mootools symlinks (if its not in the source directory)
diff --git a/web/ajax/stream.php b/web/ajax/stream.php
index 398f81147..5e9798011 100644
--- a/web/ajax/stream.php
+++ b/web/ajax/stream.php
@@ -48,7 +48,7 @@ switch ( $_REQUEST['command'] )
$remSockFile = ZM_PATH_SOCKS.'/zms-'.sprintf("%06d",$_REQUEST['connkey']).'s.sock';
$max_socket_tries = 10;
while ( !file_exists($remSockFile) && $max_socket_tries-- ) //sometimes we are too fast for our own good, if it hasn't been setup yet give it a second.
- sleep(1);
+ usleep(200000);
if ( !@socket_sendto( $socket, $msg, strlen($msg), 0, $remSockFile ) )
{
diff --git a/web/api/.htaccess b/web/api/.htaccess
index 6cc85be06..7139a2766 100644
--- a/web/api/.htaccess
+++ b/web/api/.htaccess
@@ -2,4 +2,5 @@
RewriteEngine on
RewriteRule ^$ app/webroot/ [L]
RewriteRule (.*) app/webroot/$1 [L]
+ RewriteBase /zm/api
diff --git a/web/api/CMakeLists.txt b/web/api/CMakeLists.txt
index 56bc82a50..3d865527b 100644
--- a/web/api/CMakeLists.txt
+++ b/web/api/CMakeLists.txt
@@ -10,3 +10,6 @@ configure_file(app/Config/database.php.default "${CMAKE_CURRENT_BINARY_DIR}/app/
# Configure core.php
configure_file(app/Config/core.php.default "${CMAKE_CURRENT_BINARY_DIR}/app/Config/core.php" @ONLY)
+
+# Configure bootstrap.php
+configure_file(app/Config/bootstrap.php.in "${CMAKE_CURRENT_BINARY_DIR}/app/Config/bootstrap.php" @ONLY)
diff --git a/web/api/app/.htaccess b/web/api/app/.htaccess
index fc3aac4b2..1af74d971 100644
--- a/web/api/app/.htaccess
+++ b/web/api/app/.htaccess
@@ -2,4 +2,5 @@
RewriteEngine on
RewriteRule ^$ webroot/ [L]
RewriteRule (.*) webroot/$1 [L]
-
\ No newline at end of file
+ RewriteBase /zm/api
+
diff --git a/web/api/app/Config/bootstrap.php b/web/api/app/Config/bootstrap.php.in
similarity index 80%
rename from web/api/app/Config/bootstrap.php
rename to web/api/app/Config/bootstrap.php.in
index b8932f3a6..cff9a29dd 100644
--- a/web/api/app/Config/bootstrap.php
+++ b/web/api/app/Config/bootstrap.php.in
@@ -69,6 +69,7 @@ Cache::config('default', array('engine' => 'File'));
* CakePlugin::load('DebugKit'); //Loads a single plugin named DebugKit
*
*/
+CakePlugin::load('Crud');
/**
* You can attach event listeners to the request lifecycle as Dispatcher Filter. By default CakePHP bundles two filters:
@@ -106,3 +107,37 @@ CakeLog::config('error', array(
'types' => array('warning', 'error', 'critical', 'alert', 'emergency'),
'file' => 'error',
));
+
+Configure::write('ZM_CONFIG', '@ZM_CONFIG@');
+Configure::write('ZM_VERSION', '@VERSION@');
+Configure::write('ZM_API_VERSION', '@API_VERSION@');
+
+loadConfigFile();
+
+function loadConfigFile() {
+ $configFile = Configure::read('ZM_CONFIG');
+ $localConfigFile = basename($configFile);
+ if ( file_exists( $localConfigFile ) && filesize( $localConfigFile ) > 0 )
+ {
+ if ( php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']) )
+ print( "Warning, overriding installed $localConfigFile file with local copy\n" );
+ else
+ error_log( "Warning, overriding installed $localConfigFile file with local copy" );
+ $configFile = $localConfigFile;
+ }
+
+ $cfg = fopen( $configFile, "r") or die("Could not open config file.");
+ while ( !feof($cfg) )
+ {
+ $str = fgets( $cfg, 256 );
+ if ( preg_match( '/^\s*$/', $str ))
+ continue;
+ elseif ( preg_match( '/^\s*#/', $str ))
+ continue;
+ elseif ( preg_match( '/^\s*([^=\s]+)\s*=\s*(.*?)\s*$/', $str, $matches ))
+ Configure::write("$matches[1]", "$matches[2]");
+ }
+ fclose( $cfg );
+}
+
+
diff --git a/web/api/app/Config/core.php b/web/api/app/Config/core.php
new file mode 100644
index 000000000..436cf8d60
--- /dev/null
+++ b/web/api/app/Config/core.php
@@ -0,0 +1,387 @@
+ 0
+ * and log errors with CakeLog when debug = 0.
+ *
+ * Options:
+ *
+ * - `handler` - callback - The callback to handle errors. You can set this to any callable type,
+ * including anonymous functions.
+ * Make sure you add App::uses('MyHandler', 'Error'); when using a custom handler class
+ * - `level` - integer - The level of errors you are interested in capturing.
+ * - `trace` - boolean - Include stack traces for errors in log files.
+ *
+ * @see ErrorHandler for more information on error handling and configuration.
+ */
+ Configure::write('Error', array(
+ 'handler' => 'ErrorHandler::handleError',
+ 'level' => E_ALL & ~E_DEPRECATED,
+ 'trace' => true
+ ));
+
+/**
+ * Configure the Exception handler used for uncaught exceptions. By default,
+ * ErrorHandler::handleException() is used. It will display a HTML page for the exception, and
+ * while debug > 0, framework errors like Missing Controller will be displayed. When debug = 0,
+ * framework errors will be coerced into generic HTTP errors.
+ *
+ * Options:
+ *
+ * - `handler` - callback - The callback to handle exceptions. You can set this to any callback type,
+ * including anonymous functions.
+ * Make sure you add App::uses('MyHandler', 'Error'); when using a custom handler class
+ * - `renderer` - string - The class responsible for rendering uncaught exceptions. If you choose a custom class you
+ * should place the file for that class in app/Lib/Error. This class needs to implement a render method.
+ * - `log` - boolean - Should Exceptions be logged?
+ * - `skipLog` - array - list of exceptions to skip for logging. Exceptions that
+ * extend one of the listed exceptions will also be skipped for logging.
+ * Example: `'skipLog' => array('NotFoundException', 'UnauthorizedException')`
+ *
+ * @see ErrorHandler for more information on exception handling and configuration.
+ */
+ Configure::write('Exception', array(
+ 'handler' => 'ErrorHandler::handleException',
+ 'renderer' => 'ExceptionRenderer',
+ 'log' => true
+ ));
+
+/**
+ * Application wide charset encoding
+ */
+ Configure::write('App.encoding', 'UTF-8');
+
+/**
+ * To configure CakePHP *not* to use mod_rewrite and to
+ * use CakePHP pretty URLs, remove these .htaccess
+ * files:
+ *
+ * /.htaccess
+ * /app/.htaccess
+ * /app/webroot/.htaccess
+ *
+ * And uncomment the App.baseUrl below. But keep in mind
+ * that plugin assets such as images, CSS and JavaScript files
+ * will not work without URL rewriting!
+ * To work around this issue you should either symlink or copy
+ * the plugin assets into you app's webroot directory. This is
+ * recommended even when you are using mod_rewrite. Handling static
+ * assets through the Dispatcher is incredibly inefficient and
+ * included primarily as a development convenience - and
+ * thus not recommended for production applications.
+ */
+ //Configure::write('App.baseUrl', env('SCRIPT_NAME'));
+
+/**
+ * To configure CakePHP to use a particular domain URL
+ * for any URL generation inside the application, set the following
+ * configuration variable to the http(s) address to your domain. This
+ * will override the automatic detection of full base URL and can be
+ * useful when generating links from the CLI (e.g. sending emails)
+ */
+ //Configure::write('App.fullBaseUrl', 'http://example.com');
+
+/**
+ * Web path to the public images directory under webroot.
+ * If not set defaults to 'img/'
+ */
+ //Configure::write('App.imageBaseUrl', 'img/');
+
+/**
+ * Web path to the CSS files directory under webroot.
+ * If not set defaults to 'css/'
+ */
+ //Configure::write('App.cssBaseUrl', 'css/');
+
+/**
+ * Web path to the js files directory under webroot.
+ * If not set defaults to 'js/'
+ */
+ //Configure::write('App.jsBaseUrl', 'js/');
+
+/**
+ * Uncomment the define below to use CakePHP prefix routes.
+ *
+ * The value of the define determines the names of the routes
+ * and their associated controller actions:
+ *
+ * Set to an array of prefixes you want to use in your application. Use for
+ * admin or other prefixed routes.
+ *
+ * Routing.prefixes = array('admin', 'manager');
+ *
+ * Enables:
+ * `admin_index()` and `/admin/controller/index`
+ * `manager_index()` and `/manager/controller/index`
+ *
+ */
+ //Configure::write('Routing.prefixes', array('admin'));
+
+/**
+ * Turn off all caching application-wide.
+ *
+ */
+ //Configure::write('Cache.disable', true);
+
+/**
+ * Enable cache checking.
+ *
+ * If set to true, for view caching you must still use the controller
+ * public $cacheAction inside your controllers to define caching settings.
+ * You can either set it controller-wide by setting public $cacheAction = true,
+ * or in each action using $this->cacheAction = true.
+ *
+ */
+ //Configure::write('Cache.check', true);
+
+/**
+ * Enable cache view prefixes.
+ *
+ * If set it will be prepended to the cache name for view file caching. This is
+ * helpful if you deploy the same application via multiple subdomains and languages,
+ * for instance. Each version can then have its own view cache namespace.
+ * Note: The final cache file name will then be `prefix_cachefilename`.
+ */
+ //Configure::write('Cache.viewPrefix', 'prefix');
+
+/**
+ * Session configuration.
+ *
+ * Contains an array of settings to use for session configuration. The defaults key is
+ * used to define a default preset to use for sessions, any settings declared here will override
+ * the settings of the default config.
+ *
+ * ## Options
+ *
+ * - `Session.cookie` - The name of the cookie to use. Defaults to 'CAKEPHP'
+ * - `Session.timeout` - The number of minutes you want sessions to live for. This timeout is handled by CakePHP
+ * - `Session.cookieTimeout` - The number of minutes you want session cookies to live for.
+ * - `Session.checkAgent` - Do you want the user agent to be checked when starting sessions? You might want to set the
+ * value to false, when dealing with older versions of IE, Chrome Frame or certain web-browsing devices and AJAX
+ * - `Session.defaults` - The default configuration set to use as a basis for your session.
+ * There are four builtins: php, cake, cache, database.
+ * - `Session.handler` - Can be used to enable a custom session handler. Expects an array of callables,
+ * that can be used with `session_save_handler`. Using this option will automatically add `session.save_handler`
+ * to the ini array.
+ * - `Session.autoRegenerate` - Enabling this setting, turns on automatic renewal of sessions, and
+ * sessionids that change frequently. See CakeSession::$requestCountdown.
+ * - `Session.ini` - An associative array of additional ini values to set.
+ *
+ * The built in defaults are:
+ *
+ * - 'php' - Uses settings defined in your php.ini.
+ * - 'cake' - Saves session files in CakePHP's /tmp directory.
+ * - 'database' - Uses CakePHP's database sessions.
+ * - 'cache' - Use the Cache class to save sessions.
+ *
+ * To define a custom session handler, save it at /app/Model/Datasource/Session/.php.
+ * Make sure the class implements `CakeSessionHandlerInterface` and set Session.handler to
+ *
+ * To use database sessions, run the app/Config/Schema/sessions.php schema using
+ * the cake shell command: cake schema create Sessions
+ *
+ */
+ Configure::write('Session', array(
+ 'defaults' => 'php'
+ ));
+
+/**
+ * A random string used in security hashing methods.
+ */
+ Configure::write('Security.salt', 'Q0MjGG2xRQEhJVQR85WhFJKI7f2St8RYMlVR7GNQ');
+
+/**
+ * A random numeric string (digits only) used to encrypt/decrypt strings.
+ */
+ Configure::write('Security.cipherSeed', '02670120062639232092038865362');
+
+/**
+ * Apply timestamps with the last modified time to static assets (js, css, images).
+ * Will append a query string parameter containing the time the file was modified. This is
+ * useful for invalidating browser caches.
+ *
+ * Set to `true` to apply timestamps when debug > 0. Set to 'force' to always enable
+ * timestamping regardless of debug value.
+ */
+ //Configure::write('Asset.timestamp', true);
+
+/**
+ * Compress CSS output by removing comments, whitespace, repeating tags, etc.
+ * This requires a/var/cache directory to be writable by the web server for caching.
+ * and /vendors/csspp/csspp.php
+ *
+ * To use, prefix the CSS link URL with '/ccss/' instead of '/css/' or use HtmlHelper::css().
+ */
+ //Configure::write('Asset.filter.css', 'css.php');
+
+/**
+ * Plug in your own custom JavaScript compressor by dropping a script in your webroot to handle the
+ * output, and setting the config below to the name of the script.
+ *
+ * To use, prefix your JavaScript link URLs with '/cjs/' instead of '/js/' or use JsHelper::link().
+ */
+ //Configure::write('Asset.filter.js', 'custom_javascript_output_filter.php');
+
+/**
+ * The class name and database used in CakePHP's
+ * access control lists.
+ */
+ Configure::write('Acl.classname', 'DbAcl');
+ Configure::write('Acl.database', 'default');
+
+/**
+ * Uncomment this line and correct your server timezone to fix
+ * any date & time related errors.
+ */
+ //date_default_timezone_set('UTC');
+
+/**
+ * `Config.timezone` is available in which you can set users' timezone string.
+ * If a method of CakeTime class is called with $timezone parameter as null and `Config.timezone` is set,
+ * then the value of `Config.timezone` will be used. This feature allows you to set users' timezone just
+ * once instead of passing it each time in function calls.
+ */
+ //Configure::write('Config.timezone', 'Europe/Paris');
+
+/**
+ *
+ * Cache Engine Configuration
+ * Default settings provided below
+ *
+ * File storage engine.
+ *
+ * Cache::config('default', array(
+ * 'engine' => 'File', //[required]
+ * 'duration' => 3600, //[optional]
+ * 'probability' => 100, //[optional]
+ * 'path' => CACHE, //[optional] use system tmp directory - remember to use absolute path
+ * 'prefix' => 'cake_', //[optional] prefix every cache file with this string
+ * 'lock' => false, //[optional] use file locking
+ * 'serialize' => true, //[optional]
+ * 'mask' => 0664, //[optional]
+ * ));
+ *
+ * APC (http://pecl.php.net/package/APC)
+ *
+ * Cache::config('default', array(
+ * 'engine' => 'Apc', //[required]
+ * 'duration' => 3600, //[optional]
+ * 'probability' => 100, //[optional]
+ * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string
+ * ));
+ *
+ * Xcache (http://xcache.lighttpd.net/)
+ *
+ * Cache::config('default', array(
+ * 'engine' => 'Xcache', //[required]
+ * 'duration' => 3600, //[optional]
+ * 'probability' => 100, //[optional]
+ * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string
+ * 'user' => 'user', //user from xcache.admin.user settings
+ * 'password' => 'password', //plaintext password (xcache.admin.pass)
+ * ));
+ *
+ * Memcached (http://www.danga.com/memcached/)
+ *
+ * Uses the memcached extension. See http://php.net/memcached
+ *
+ * Cache::config('default', array(
+ * 'engine' => 'Memcached', //[required]
+ * 'duration' => 3600, //[optional]
+ * 'probability' => 100, //[optional]
+ * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string
+ * 'servers' => array(
+ * '127.0.0.1:11211' // localhost, default port 11211
+ * ), //[optional]
+ * 'persistent' => 'my_connection', // [optional] The name of the persistent connection.
+ * 'compress' => false, // [optional] compress data in Memcached (slower, but uses less memory)
+ * ));
+ *
+ * Wincache (http://php.net/wincache)
+ *
+ * Cache::config('default', array(
+ * 'engine' => 'Wincache', //[required]
+ * 'duration' => 3600, //[optional]
+ * 'probability' => 100, //[optional]
+ * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string
+ * ));
+ */
+
+/**
+ * Configure the cache handlers that CakePHP will use for internal
+ * metadata like class maps, and model schema.
+ *
+ * By default File is used, but for improved performance you should use APC.
+ *
+ * Note: 'default' and other application caches should be configured in app/Config/bootstrap.php.
+ * Please check the comments in bootstrap.php for more info on the cache engines available
+ * and their settings.
+ */
+$engine = 'File';
+
+// In development mode, caches should expire quickly.
+$duration = '+999 days';
+if (Configure::read('debug') > 0) {
+ $duration = '+10 seconds';
+}
+
+// Prefix each application on the same server with a different string, to avoid Memcache and APC conflicts.
+$prefix = 'myapp_';
+
+/**
+ * Configure the cache used for general framework caching. Path information,
+ * object listings, and translation cache files are stored with this configuration.
+ */
+Cache::config('_cake_core_', array(
+ 'engine' => $engine,
+ 'prefix' => $prefix . 'cake_core_',
+ 'path' => CACHE . 'persistent' . DS,
+ 'serialize' => ($engine === 'File'),
+ 'duration' => $duration
+));
+
+/**
+ * Configure the cache for model and datasource caches. This cache configuration
+ * is used to store schema descriptions, and table listings in connections.
+ */
+Cache::config('_cake_model_', array(
+ 'engine' => $engine,
+ 'prefix' => $prefix . 'cake_model_',
+ 'path' => CACHE . 'models' . DS,
+ 'serialize' => ($engine === 'File'),
+ 'duration' => $duration
+));
diff --git a/web/api/app/Config/routes.php b/web/api/app/Config/routes.php
index ed2d1f2ca..0f9343644 100644
--- a/web/api/app/Config/routes.php
+++ b/web/api/app/Config/routes.php
@@ -28,6 +28,14 @@
Router::mapResources('configs');
Router::mapResources('events');
Router::mapResources('frames');
+ Router::mapResources('host');
+ Router::mapResources('logs');
+ Router::mapResources('states');
+ Router::mapResources('zonepresets');
+
+ /* Add new API to retrieve camera controls - for PTZ */
+ /* refer to https://github.com/ZoneMinder/ZoneMinder/issues/799#issuecomment-105233112 */
+ Router::mapResources('controls');
Router::parseExtensions();
/**
diff --git a/web/api/app/Controller/AppController.php b/web/api/app/Controller/AppController.php
index a827969e1..f3011e6ff 100644
--- a/web/api/app/Controller/AppController.php
+++ b/web/api/app/Controller/AppController.php
@@ -18,8 +18,8 @@
* @since CakePHP(tm) v 0.2.9
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
-
App::uses('Controller', 'Controller');
+App::uses('CrudControllerTrait', 'Crud.Lib');
/**
* Application Controller
@@ -31,4 +31,20 @@ App::uses('Controller', 'Controller');
* @link http://book.cakephp.org/2.0/en/controllers.html#the-app-controller
*/
class AppController extends Controller {
+ use CrudControllerTrait;
+
+ public $components = [
+ 'RequestHandler',
+ 'Crud.Crud' => [
+ 'actions' => [
+ 'index' => 'Crud.Index',
+ 'add' => 'Crud.Add',
+ 'edit' => 'Crud.Edit',
+ 'view' => 'Crud.View',
+ 'keyvalue' => 'Crud.List',
+ 'category' => 'Crud.Category'
+ ],
+ 'listeners' => ['Api', 'ApiTransformation']
+ ]
+ ];
}
diff --git a/web/api/app/Controller/Component/ConfigParserComponent.php b/web/api/app/Controller/Component/ConfigParserComponent.php
index 18d06fba8..56a2039a0 100644
--- a/web/api/app/Controller/Component/ConfigParserComponent.php
+++ b/web/api/app/Controller/Component/ConfigParserComponent.php
@@ -24,7 +24,7 @@ class ConfigParserComponent extends Component {
public function getInput($name, $type, $id) {
if ($type == 'checkbox') {
- $string = '';
+ $string = '';
} elseif ($type == 'text') {
$string = '';
} elseif ($type == 'textarea') {
diff --git a/web/api/app/Controller/Component/FilterComponent.php b/web/api/app/Controller/Component/FilterComponent.php
new file mode 100644
index 000000000..bf6e423f7
--- /dev/null
+++ b/web/api/app/Controller/Component/FilterComponent.php
@@ -0,0 +1,34 @@
+ $value) {
+ // If the named param contains an array, we want to turn it into an IN condition
+ // Otherwise, we add it right into the $conditions array
+ if (is_array($value)) {
+ $array = array();
+
+ foreach ($value as $term) {
+ array_push($array, $term);
+ }
+
+ $query = array($attribute => $array);
+ array_push($conditions, $query);
+ } else {
+ array_push($conditions, array($attribute => $value));
+ }
+ }
+
+ }
+
+ return $conditions;
+ }
+
+}
+?>
diff --git a/web/api/app/Controller/Component/ImageComponent.php b/web/api/app/Controller/Component/ImageComponent.php
new file mode 100644
index 000000000..68a254031
--- /dev/null
+++ b/web/api/app/Controller/Component/ImageComponent.php
@@ -0,0 +1,100 @@
+getEventPath($event);
+
+ $captImage = sprintf( "%0".$config['ZM_EVENT_IMAGE_DIGITS']."d-capture.jpg", $frame['Frame']['FrameId'] );
+ $captPath = $eventPath.'/'.$captImage;
+ $thumbCaptPath = $config['ZM_DIR_IMAGES'].'/'.$event['Event']['Id'].'-'.$captImage;
+
+ $analImage = sprintf( "%0".$config['ZM_EVENT_IMAGE_DIGITS']."d-analyse.jpg", $frame['Frame']['FrameId'] );
+ $analPath = $eventPath.'/'.$analImage;
+ $analFile = $config['ZM_DIR_EVENTS']."/".$analPath;
+ $thumbAnalPath = $config['ZM_DIR_IMAGES'].'/'.$event['Event']['Id'].'-'.$analImage;
+
+ $alarmFrame = $frame['Frame']['Type']=='Alarm';
+
+ $hasAnalImage = $alarmFrame && file_exists( $analFile ) && filesize( $analFile );
+ $isAnalImage = $hasAnalImage && !$captureOnly;
+
+
+ if ( !$config['ZM_WEB_SCALE_THUMBS'] || $scale >= 100 || !function_exists( 'imagecreatefromjpeg' ) ) {
+ $imagePath = $thumbPath = $isAnalImage?$analPath:$captPath;
+ $imageFile = $config['ZM_DIR_EVENTS']."/".$imagePath;
+ $thumbFile = $config['ZM_DIR_EVENTS']."/".$thumbPath;
+ } else {
+ if ( version_compare( phpversion(), "4.3.10", ">=") )
+ $fraction = sprintf( "%.3F", $scale/100 );
+ else
+ $fraction = sprintf( "%.3f", $scale/100 );
+ $scale = (int)round( $scale );
+
+ $thumbCaptPath = preg_replace( "/\.jpg$/", "-$scale.jpg", $thumbCaptPath );
+ $thumbAnalPath = preg_replace( "/\.jpg$/", "-$scale.jpg", $thumbAnalPath );
+
+ if ( $isAnalImage )
+ {
+ $imagePath = $analPath;
+ $thumbPath = $thumbAnalPath;
+ }
+ else
+ {
+ $imagePath = $captPath;
+ $thumbPath = $thumbCaptPath;
+ }
+
+ $imageFile = $config['ZM_DIR_EVENTS']."/".$imagePath;
+ //$thumbFile = ZM_DIR_EVENTS."/".$thumbPath;
+ $thumbFile = $thumbPath;
+ // PP: This segment of code results in errors when trying to get Events API
+ // This actually seems to be generating images for the angular UI web view
+ // and should not be a part of the API anyway
+ // I've commented it so events APIs continue to work
+ // I did ask Kyle about this, but I don't have an answer from him
+ // Either way, it does no harm to remove it -- as the UI of master
+ // does not use API code anyway
+ /*
+ if ( $overwrite || !file_exists( $thumbFile ) || !filesize( $thumbFile ) )
+ {
+ // Get new dimensions
+ list( $imageWidth, $imageHeight ) = getimagesize( $imageFile );
+ $thumbWidth = $imageWidth * $fraction;
+ $thumbHeight = $imageHeight * $fraction;
+
+ // Resample
+ $thumbImage = imagecreatetruecolor( $thumbWidth, $thumbHeight );
+ $image = imagecreatefromjpeg( $imageFile );
+ imagecopyresampled( $thumbImage, $image, 0, 0, 0, 0, $thumbWidth, $thumbHeight, $imageWidth, $imageHeight );
+
+ if ( !imagejpeg( $thumbImage, $thumbFile ) )
+ Error( "Can't create thumbnail '$thumbPath'" );
+ }
+ */
+ }
+
+ /*
+ $imageData = array(
+ 'eventPath' => $eventPath,
+ 'imagePath' => $imagePath,
+ 'thumbPath' => $thumbPath,
+ 'imageFile' => $imageFile,
+ 'thumbFile' => $thumbFile,
+ 'imageClass' => $alarmFrame?"alarm":"normal",
+ 'isAnalImage' => $isAnalImage,
+ 'hasAnalImage' => $hasAnalImage,
+ );
+
+ return( $imageData );
+ */
+
+ }
+
+ // Take the StartTime of an Event and return
+ // the path to its location on the filesystem
+ public function getEventPath( $event ) {
+ return $event['Event']['MonitorId'].'/'.strftime( "%y/%m/%d/%H/%M/%S", strtotime($event['Event']['StartTime']) );
+ }
+}
+?>
diff --git a/web/api/app/Controller/Component/ScalerComponent.php b/web/api/app/Controller/Component/ScalerComponent.php
new file mode 100644
index 000000000..30678dad6
--- /dev/null
+++ b/web/api/app/Controller/Component/ScalerComponent.php
@@ -0,0 +1,27 @@
+
diff --git a/web/api/app/Controller/ConfigsController.php b/web/api/app/Controller/ConfigsController.php
index 3198ae58e..651f5724e 100644
--- a/web/api/app/Controller/ConfigsController.php
+++ b/web/api/app/Controller/ConfigsController.php
@@ -14,20 +14,6 @@ class ConfigsController extends AppController {
*/
public $components = array('RequestHandler');
-/**
- * index method
- *
- * @return void
- */
- public function index() {
- $this->Config->recursive = 0;
- $configs = $this->Config->find('all');
- $this->set(array(
- 'configs' => $configs,
- '_serialize' => array('configs')
- ));
- }
-
/**
* view method
*
@@ -47,6 +33,19 @@ class ConfigsController extends AppController {
));
}
+ public function viewByName($name = null) {
+ $config = $this->Config->findByName($name, array('fields' => 'Value'));
+
+ if (!$config) {
+ throw new NotFoundException(__('Invalid config'));
+ }
+
+ $this->set(array(
+ 'config' => $config['Config'],
+ '_serialize' => array('config')
+ ));
+ }
+
/**
* edit method
*
@@ -93,45 +92,19 @@ class ConfigsController extends AppController {
/**
* categories method
*
- * Either return a list of distinct categories
- * Or all configs under a certain category
+ * return a list of distinct categories
*/
public function categories($category = null) {
- if ($category != null) {
- if (!$this->Config->find('first', array( 'conditions' => array('Config.Category' => $category)))) {
- throw new NotFoundException(__('Invalid Config Category'));
- }
-
- $config = $this->Config->find('all', array(
- 'conditions' => array('Config.Category' => $category),
- 'recursive' => 0
- ));
- $this->set(array(
- 'config' => $config,
- '_serialize' => array('config')
- ));
- } else {
- $categories = $this->Config->find('all', array(
- 'fields' => array('DISTINCT Config.Category'),
- 'conditions' => array('Config.Category !=' => 'hidden'),
- 'recursive' => 0
- ));
- $this->set(array(
- 'categories' => $categories,
- '_serialize' => array('categories')
- ));
- }
-
- }
-
- public function keyValue() {
- $keyValues = $this->Config->find('list', array(
- 'fields' => array('Config.Name', 'Config.Value')
+ $categories = $this->Config->find('all', array(
+ 'fields' => array('DISTINCT Config.Category'),
+ 'conditions' => array('Config.Category !=' => 'hidden'),
+ 'recursive' => 0
));
$this->set(array(
- 'keyValues' => $keyValues,
- '_serialize' => array('keyValues')
+ 'categories' => $categories,
+ '_serialize' => array('categories')
));
}
}
+
diff --git a/web/api/app/Controller/ControlsController.php b/web/api/app/Controller/ControlsController.php
new file mode 100644
index 000000000..879142f75
--- /dev/null
+++ b/web/api/app/Controller/ControlsController.php
@@ -0,0 +1,59 @@
+Control->recursive = 0;
+ $controls = $this->Control->find('all');
+ $this->set(array(
+ 'controls' => $controls,
+ '_serialize' => array('controls')
+ ));
+ }
+
+/**
+ * view method
+ *
+ * @throws NotFoundException
+ * @param string $id
+ * @return void
+ */
+ public function view($id = null) {
+ if (!$this->Control->exists($id)) {
+ throw new NotFoundException(__('Invalid control'));
+ }
+ $options = array('conditions' => array('Control.' . $this->Control->primaryKey => $id));
+ $control = $this->Control->find('first', $options);
+ $this->set(array(
+ 'control' => $control,
+ '_serialize' => array('control')
+ ));
+ }
+}
+
diff --git a/web/api/app/Controller/EventsController.php b/web/api/app/Controller/EventsController.php
index 394e61caf..05a280be3 100644
--- a/web/api/app/Controller/EventsController.php
+++ b/web/api/app/Controller/EventsController.php
@@ -12,20 +12,46 @@ class EventsController extends AppController {
*
* @var array
*/
- public $components = array('RequestHandler');
+ public $components = array('RequestHandler', 'Scaler', 'Image', 'Paginator');
/**
* index method
*
* @return void
+ * This also creates a thumbnail for each event.
*/
public function index() {
$this->Event->recursive = -1;
- $events = $this->Event->find('all');
- $this->set(array(
- 'events' => $events,
- '_serialize' => array('events')
+
+ if ($this->request->params['named']) {
+ $this->FilterComponent = $this->Components->load('Filter');
+ $conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
+ } else {
+ $conditions = array();
+ }
+
+ // How many events to return
+ $this->loadModel('Config');
+ $limit = $this->Config->find('list', array(
+ 'conditions' => array('Name' => 'ZM_WEB_EVENTS_PER_PAGE'),
+ 'fields' => array('Name', 'Value')
));
+ $this->Paginator->settings = array(
+ 'limit' => $limit['ZM_WEB_EVENTS_PER_PAGE'],
+ 'order' => array('StartTime', 'MaxScore'),
+ 'paramType' => 'querystring',
+ 'conditions' => $conditions
+ );
+ $events = $this->Paginator->paginate('Event');
+
+ // For each event, get its thumbnail data (path, width, height)
+ foreach ($events as $key => $value) {
+ $thumbData = $this->createThumbnail($value['Event']['Id']);
+ $events[$key]['thumbData'] = $thumbData;
+
+ }
+
+ $this->set(compact('events'));
}
/**
@@ -36,12 +62,23 @@ class EventsController extends AppController {
* @return void
*/
public function view($id = null) {
- $this->Event->recursive = -1;
+ $this->loadModel('Config');
+ $configs = $this->Config->find('list', array(
+ 'fields' => array('Name', 'Value'),
+ 'conditions' => array('Name' => array('ZM_DIR_EVENTS'))
+ ));
+
+ $this->Event->recursive = 1;
if (!$this->Event->exists($id)) {
throw new NotFoundException(__('Invalid event'));
}
$options = array('conditions' => array('Event.' . $this->Event->primaryKey => $id));
$event = $this->Event->find('first', $options);
+
+ $path = $configs['ZM_DIR_EVENTS'].'/'.$this->Image->getEventPath($event).'/';
+
+ $event['Event']['BasePath'] = $path;
+
$this->set(array(
'event' => $event,
'_serialize' => array('event')
@@ -108,4 +145,142 @@ class EventsController extends AppController {
} else {
return $this->flash(__('The event could not be deleted. Please, try again.'), array('action' => 'index'));
}
- }}
+ }
+
+ public function search() {
+ $this->Event->recursive = -1;
+ $conditions = array();
+
+ foreach ($this->params['named'] as $param_name => $value) {
+ // Transform params into mysql
+ if (preg_match("/interval/i", $value, $matches)) {
+ $condition = array("$param_name >= (date_sub(now(), $value))");
+ } else {
+ $condition = array($param_name => $value);
+ }
+ array_push($conditions, $condition);
+ }
+
+ $results = $this->Event->find('all', array(
+ 'conditions' => $conditions
+ ));
+
+ $this->set(array(
+ 'results' => $results,
+ '_serialize' => array('results')
+ ));
+
+
+ }
+
+ public function consoleEvents($interval = null) {
+ $this->Event->recursive = -1;
+ $results = array();
+
+ $query = $this->Event->query("select MonitorId, COUNT(*) AS Count from Events WHERE StartTime >= (DATE_SUB(NOW(), interval $interval)) GROUP BY MonitorId;");
+
+ foreach ($query as $result) {
+ $results[$result['Events']['MonitorId']] = $result[0]['Count'];
+ }
+
+ $this->set(array(
+ 'results' => $results,
+ '_serialize' => array('results')
+ ));
+ }
+
+ // Create a thumbnail and return the thumbnail's data for a given event id.
+ public function createThumbnail($id = null) {
+ $this->Event->recursive = -1;
+
+ if (!$this->Event->exists($id)) {
+ throw new NotFoundException(__('Invalid event'));
+ }
+
+ $event = $this->Event->find('first', array(
+ 'conditions' => array('Id' => $id)
+ ));
+
+ // Find the max Frame for this Event. Error out otherwise.
+ $this->loadModel('Frame');
+ if (! $frame = $this->Frame->find('first', array(
+ 'conditions' => array(
+ 'EventId' => $event['Event']['Id'],
+ 'Score' => $event['Event']['MaxScore']
+ )
+ ))) {
+ throw new NotFoundException(__("Can not find Frame for Event " . $event['Event']['Id']));
+ }
+
+ $this->loadModel('Config');
+
+ // Get the config options required for reScale and getImageSrc
+ // The $bw, $thumbs and unset() code is a workaround / temporary
+ // until I have a better way of handing per-bandwidth config options
+ $bw = (isset($_COOKIE['zmBandwidth']) ? strtoupper(substr($_COOKIE['zmBandwidth'], 0, 1)) : 'L');
+ $thumbs = "ZM_WEB_${bw}_SCALE_THUMBS";
+
+ $config = $this->Config->find('list', array(
+ 'conditions' => array('OR' => array(
+ 'Name' => array('ZM_WEB_LIST_THUMB_WIDTH',
+ 'ZM_WEB_LIST_THUMB_HEIGHT',
+ 'ZM_EVENT_IMAGE_DIGITS',
+ 'ZM_DIR_IMAGES',
+ "$thumbs",
+ 'ZM_DIR_EVENTS'
+ )
+ )),
+ 'fields' => array('Name', 'Value')
+ ));
+ $config['ZM_WEB_SCALE_THUMBS'] = $config[$thumbs];
+ unset($config[$thumbs]);
+
+ // reScale based on either the width, or the hight, of the event.
+ if ( $config['ZM_WEB_LIST_THUMB_WIDTH'] ) {
+ $thumbWidth = $config['ZM_WEB_LIST_THUMB_WIDTH'];
+ $scale = (100 * $thumbWidth) / $event['Event']['Width'];
+ $thumbHeight = $this->Scaler->reScale( $event['Event']['Height'], $scale );
+ }
+ elseif ( $config['ZM_WEB_LIST_THUMB_HEIGHT'] ) {
+ $thumbHeight = $config['ZM_WEB_LIST_THUMB_HEIGHT'];
+ $scale = (100*$thumbHeight)/$event['Event']['Height'];
+ $thumbWidth = $this->Scaler->reScale( $event['Event']['Width'], $scale );
+ }
+ else {
+ throw new NotFoundException(__('No thumbnail width or height specified, please check in Options->Web'));
+ }
+
+ $imageData = $this->Image->getImageSrc( $event, $frame, $scale, $config );
+ $thumbData['Path'] = $imageData['thumbPath'];
+ $thumbData['Width'] = (int)$thumbWidth;
+ $thumbData['Height'] = (int)$thumbHeight;
+
+ return( $thumbData );
+
+ }
+
+ public function archive($id = null) {
+ $this->Event->recursive = -1;
+ if (!$this->Event->exists($id)) {
+ throw new NotFoundException(__('Invalid event'));
+ }
+
+ // Get the current value of Archive
+ $archived = $this->Event->find('first', array(
+ 'fields' => array('Event.Archived'),
+ 'conditions' => array('Event.Id' => $id)
+ ));
+ // If 0, 1, if 1, 0
+ $archiveVal = (($archived['Event']['Archived'] == 0) ? 1 : 0);
+
+ // Save the new value
+ $this->Event->id = $id;
+ $this->Event->saveField('Archived', $archiveVal);
+
+ $this->set(array(
+ 'archived' => $archiveVal,
+ '_serialize' => array('archived')
+ ));
+ }
+
+}
diff --git a/web/api/app/Controller/HostController.php b/web/api/app/Controller/HostController.php
new file mode 100644
index 000000000..ee5ecd30f
--- /dev/null
+++ b/web/api/app/Controller/HostController.php
@@ -0,0 +1,112 @@
+set(array(
+ 'result' => $result,
+ '_serialize' => array('result')
+ ));
+ }
+
+ function getLoad() {
+ $load = sys_getloadavg();
+
+ $this->set(array(
+ 'load' => $load,
+ '_serialize' => array('load')
+ ));
+ }
+
+ // If $mid is set, only return disk usage for that monitor
+ // Else, return an array of total disk usage, and per-monitor
+ // usage.
+ function getDiskPercent($mid = null) {
+ $this->loadModel('Config');
+ $this->loadModel('Monitor');
+
+ // If $mid is passed, see if it is valid
+ if ($mid) {
+ if (!$this->Monitor->exists($mid)) {
+ throw new NotFoundException(__('Invalid monitor'));
+ }
+ }
+
+ $zm_dir_events = $this->Config->find('list', array(
+ 'conditions' => array('Name' => 'ZM_DIR_EVENTS'),
+ 'fields' => array('Name', 'Value')
+ ));
+ $zm_dir_events = $zm_dir_events['ZM_DIR_EVENTS' ];
+
+ // Test to see if $zm_dir_events is relative or absolute
+ if ('/' === "" || strrpos($zm_dir_events, '/', -strlen($zm_dir_events)) !== TRUE) {
+ // relative - so add the full path
+ $zm_dir_events = Configure::read('ZM_PATH_WEB') . '/' . $zm_dir_events;
+ }
+
+ if ($mid) {
+ // Get disk usage for $mid
+ $usage = shell_exec ("du -sh0 $zm_dir_events/$mid | awk '{print $1}'");
+ } else {
+ $monitors = $this->Monitor->find('all', array(
+ 'fields' => array('Id', 'Name', 'WebColour')
+ ));
+ $usage = array();
+
+ // Add each monitor's usage to array
+ foreach ($monitors as $key => $value) {
+ $id = $value['Monitor']['Id'];
+ $name = $value['Monitor']['Name'];
+ $color = $value['Monitor']['WebColour'];
+
+ $space = shell_exec ("du -s0 $zm_dir_events/$id | awk '{print $1}'");
+ if ($space == null) {
+ $space = 0;
+ }
+ $space = $space/1024/1024;
+
+ $usage[$name] = array(
+ 'space' => rtrim($space),
+ 'color' => $color
+ );
+ }
+
+ // Add total usage to array
+ $space = shell_exec( "df $zm_dir_events |tail -n1 | awk '{print $3 }'");
+ $space = $space/1024/1024;
+ $usage['Total'] = array(
+ 'space' => rtrim($space),
+ 'color' => '#F7464A'
+ );
+ }
+
+ $this->set(array(
+ 'usage' => $usage,
+ '_serialize' => array('usage')
+ ));
+ }
+
+ function getVersion() {
+ $version = Configure::read('ZM_VERSION');
+ $apiversion = Configure::read('ZM_API_VERSION');
+
+ $this->set(array(
+ 'version' => $version,
+ 'apiversion' => $apiversion,
+ '_serialize' => array('version', 'apiversion')
+ ));
+ }
+}
diff --git a/web/api/app/Controller/LogsController.php b/web/api/app/Controller/LogsController.php
index 908c635e2..bb8f47b80 100644
--- a/web/api/app/Controller/LogsController.php
+++ b/web/api/app/Controller/LogsController.php
@@ -13,7 +13,12 @@ class LogsController extends AppController {
*
* @var array
*/
- public $components = array('Paginator');
+ public $components = array('Paginator', 'RequestHandler');
+ public $paginate = array(
+ 'limit' => 100,
+ 'order' => array( 'Log.TimeKey' => 'asc' ),
+ 'paramType' => 'querystring'
+ );
/**
* index method
@@ -21,8 +26,11 @@ class LogsController extends AppController {
* @return void
*/
public function index() {
- $this->Log->recursive = 0;
- $this->set('logs', $this->Paginator->paginate());
+ $this->Log->recursive = -1;
+ $this->Paginator->settings = $this->paginate;
+
+ $logs = $this->Paginator->paginate('Log');
+ $this->set(compact('logs'));
}
/**
diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php
index da4339e8e..0588a8cc2 100644
--- a/web/api/app/Controller/MonitorsController.php
+++ b/web/api/app/Controller/MonitorsController.php
@@ -59,6 +59,7 @@ class MonitorsController extends AppController {
if ($this->request->is('post')) {
$this->Monitor->create();
if ($this->Monitor->save($this->request->data)) {
+ $this->daemonControl($this->Monitor->id, 'start', $this->request->data);
return $this->flash(__('The monitor has been saved.'), array('action' => 'index'));
}
}
@@ -103,6 +104,9 @@ class MonitorsController extends AppController {
throw new NotFoundException(__('Invalid monitor'));
}
$this->request->allowMethod('post', 'delete');
+
+ $this->daemonControl($this->Monitor->id, 'stop');
+
if ($this->Monitor->delete()) {
return $this->flash(__('The monitor has been deleted.'), array('action' => 'index'));
} else {
@@ -124,5 +128,81 @@ class MonitorsController extends AppController {
));
}
+ // Check if a daemon is running for the monitor id
+ public function daemonStatus() {
+ $id = $this->request->params['named']['id'];
+ $daemon = $this->request->params['named']['daemon'];
+
+ if (!$this->Monitor->exists($id)) {
+ throw new NotFoundException(__('Invalid monitor'));
+ }
+
+ $monitor = $this->Monitor->find('first', array(
+ 'fields' => array('Id', 'Type', 'Device'),
+ 'conditions' => array('Id' => $id)
+ ));
+
+ // Clean up the returned array
+ $monitor = Set::extract('/Monitor/.', $monitor);
+
+ // Pass -d for local, otherwise -m
+ if ($monitor[0]['Type'] == 'Local') {
+ $args = "-d ". $monitor[0]['Device'];
+ } else {
+ $args = "-m ". $monitor[0]['Id'];
+ }
+
+ // Build the command, and execute it
+ $zm_path_bin = Configure::read('ZM_PATH_BIN');
+ $command = escapeshellcmd("$zm_path_bin/zmdc.pl status $daemon $args");
+ $status = exec( $command );
+
+ // If 'not' is present, the daemon is not running, so return false
+ // https://github.com/ZoneMinder/ZoneMinder/issues/799#issuecomment-108996075
+ // Also sending back the status text so we can check if the monitor is in pending
+ // state which means there may be an error
+ $statustext = $status;
+ $status = (strpos($status, 'not')) ? false : true;
+
+ $this->set(array(
+ 'status' => $status,
+ 'statustext' => $statustext,
+ '_serialize' => array('status','statustext'),
+ ));
+ }
+
+ public function daemonControl($id, $command, $monitor=null, $daemon=null) {
+ $args = '';
+ $daemons = array();
+
+ if (!$monitor) {
+ // Need to see if it is local or remote
+ $monitor = $this->Monitor->find('first', array(
+ 'fields' => array('Type', 'Function'),
+ 'conditions' => array('Id' => $id)
+ ));
+ $monitor = $monitor['Monitor'];
+ }
+
+ if ($monitor['Type'] == 'Local') {
+ $args = "-d " . $monitor['Device'];
+ } else {
+ $args = "-m " . $id;
+ }
+
+ if ($monitor['Function'] == 'Monitor') {
+ array_push($daemons, 'zmc');
+ } else {
+ array_push($daemons, 'zmc', 'zma');
+ }
+
+ $zm_path_bin = Configure::read('ZM_PATH_BIN');
+
+ foreach ($daemons as $daemon) {
+ $shellcmd = escapeshellcmd("$zm_path_bin/zmdc.pl $command $daemon $args");
+ $status = exec( $shellcmd );
+ }
+ }
+
}
diff --git a/web/api/app/Controller/StatesController.php b/web/api/app/Controller/StatesController.php
new file mode 100644
index 000000000..2b007f08f
--- /dev/null
+++ b/web/api/app/Controller/StatesController.php
@@ -0,0 +1,117 @@
+State->recursive = 0;
+ $states = $this->State->find('all');
+ $this->set(array(
+ 'states' => $states,
+ '_serialize' => array('states')
+ ));
+ }
+
+/**
+ * view method
+ *
+ * @throws NotFoundException
+ * @param string $id
+ * @return void
+ */
+ public function view($id = null) {
+ if (!$this->State->exists($id)) {
+ throw new NotFoundException(__('Invalid state'));
+ }
+ $options = array('conditions' => array('State.' . $this->State->primaryKey => $id));
+ $this->set('state', $this->State->find('first', $options));
+ }
+
+/**
+ * add method
+ *
+ * @return void
+ */
+ public function add() {
+ if ($this->request->is('post')) {
+ $this->State->create();
+ if ($this->State->save($this->request->data)) {
+ return $this->flash(__('The state has been saved.'), array('action' => 'index'));
+ }
+ }
+ }
+
+/**
+ * edit method
+ *
+ * @throws NotFoundException
+ * @param string $id
+ * @return void
+ */
+ public function edit($id = null) {
+ if (!$this->State->exists($id)) {
+ throw new NotFoundException(__('Invalid state'));
+ }
+ if ($this->request->is(array('post', 'put'))) {
+ if ($this->State->save($this->request->data)) {
+ return $this->flash(__('The state has been saved.'), array('action' => 'index'));
+ }
+ } else {
+ $options = array('conditions' => array('State.' . $this->State->primaryKey => $id));
+ $this->request->data = $this->State->find('first', $options);
+ }
+ }
+
+/**
+ * delete method
+ *
+ * @throws NotFoundException
+ * @param string $id
+ * @return void
+ */
+ public function delete($id = null) {
+ $this->State->id = $id;
+ if (!$this->State->exists()) {
+ throw new NotFoundException(__('Invalid state'));
+ }
+ $this->request->allowMethod('post', 'delete');
+ if ($this->State->delete()) {
+ return $this->flash(__('The state has been deleted.'), array('action' => 'index'));
+ } else {
+ return $this->flash(__('The state could not be deleted. Please, try again.'), array('action' => 'index'));
+ }
+ }
+
+ public function change() {
+ $newState = $this->request->params['pass'][0];
+ $blah = $this->packageControl($newState);
+
+ $this->set(array(
+ 'blah' => $blah,
+ '_serialize' => array('blah')
+ ));
+ }
+
+ public function packageControl( $command ) {
+ $zm_path_bin = Configure::read('ZM_PATH_BIN');
+ $string = $zm_path_bin.'/zmpkg.pl '.escapeshellarg( $command );
+ $status = exec( $string );
+
+ return $status;
+ }
+
+
+}
diff --git a/web/api/app/Controller/ZonePresetsController.php b/web/api/app/Controller/ZonePresetsController.php
new file mode 100644
index 000000000..b89a6c75d
--- /dev/null
+++ b/web/api/app/Controller/ZonePresetsController.php
@@ -0,0 +1,99 @@
+ZonePreset->find('all');
+ $this->set(array(
+ 'zonePresets' => $zonePresets,
+ '_serialize' => array('zonePresets')
+ ));
+ }
+
+/**
+ * view method
+ *
+ * @throws NotFoundException
+ * @param string $id
+ * @return void
+ */
+ public function view($id = null) {
+ if (!$this->ZonePreset->exists($id)) {
+ throw new NotFoundException(__('Invalid zone preset'));
+ }
+ $options = array('conditions' => array('ZonePreset.' . $this->ZonePreset->primaryKey => $id));
+ $this->set('zonePreset', $this->ZonePreset->find('first', $options));
+ }
+
+/**
+ * add method
+ *
+ * @return void
+ */
+ public function add() {
+ if ($this->request->is('post')) {
+ $this->ZonePreset->create();
+ if ($this->ZonePreset->save($this->request->data)) {
+ return $this->flash(__('The zone preset has been saved.'), array('action' => 'index'));
+ }
+ }
+ }
+
+/**
+ * edit method
+ *
+ * @throws NotFoundException
+ * @param string $id
+ * @return void
+ */
+ public function edit($id = null) {
+ if (!$this->ZonePreset->exists($id)) {
+ throw new NotFoundException(__('Invalid zone preset'));
+ }
+ if ($this->request->is(array('post', 'put'))) {
+ if ($this->ZonePreset->save($this->request->data)) {
+ return $this->flash(__('The zone preset has been saved.'), array('action' => 'index'));
+ }
+ } else {
+ $options = array('conditions' => array('ZonePreset.' . $this->ZonePreset->primaryKey => $id));
+ $this->request->data = $this->ZonePreset->find('first', $options);
+ }
+ }
+
+/**
+ * delete method
+ *
+ * @throws NotFoundException
+ * @param string $id
+ * @return void
+ */
+ public function delete($id = null) {
+ $this->ZonePreset->id = $id;
+ if (!$this->ZonePreset->exists()) {
+ throw new NotFoundException(__('Invalid zone preset'));
+ }
+ $this->request->allowMethod('post', 'delete');
+ if ($this->ZonePreset->delete()) {
+ return $this->flash(__('The zone preset has been deleted.'), array('action' => 'index'));
+ } else {
+ return $this->flash(__('The zone preset could not be deleted. Please, try again.'), array('action' => 'index'));
+ }
+ }}
diff --git a/web/api/app/Controller/ZonesController.php b/web/api/app/Controller/ZonesController.php
index dc30a382a..be80c57fc 100644
--- a/web/api/app/Controller/ZonesController.php
+++ b/web/api/app/Controller/ZonesController.php
@@ -4,51 +4,26 @@ App::uses('AppController', 'Controller');
* Zones Controller
*
* @property Zone $Zone
- * @property PaginatorComponent $Paginator
*/
class ZonesController extends AppController {
-/**
- * Components
- *
- * @var array
- */
- public $components = array('Paginator', 'RequestHandler');
-
-/**
- * index method
- *
- * @return void
- */
- public function index() {
- $this->Zone->recursive = -1;
- $zones = $this->Zone->find('all');
- $this->set(array(
- 'zones' => $zones,
- '_serialize' => array('zones')
- ));
- }
-
-/**
- * view method
- *
- * @throws NotFoundException
- * @param string $id
- * @return void
- */
- public function view($id = null) {
- $this->Zone->recursive = -1;
- if (!$this->Zone->exists($id)) {
- throw new NotFoundException(__('Invalid zone'));
+// Find all zones which belong to a MonitorId
+ public function forMonitor($id = null) {
+ $this->loadModel('Monitor');
+ if (!$this->Monitor->exists($id)) {
+ throw new NotFoundException(__('Invalid monitor'));
}
- $options = array('conditions' => array('Zone.' . $this->Zone->primaryKey => $id));
- $zone = $this->Zone->find('first', $options);
+
+ $this->Zone->recursive = -1;
+
+ $zones = $this->Zone->find('all', array(
+ 'conditions' => array('MonitorId' => $id)
+ ));
$this->set(array(
- 'zone' => $zone,
- '_serialize' => array('zone')
+ 'zones' => $zones,
+ '_serialize' => array('zones')
));
}
-
/**
* add method
*
@@ -108,4 +83,38 @@ class ZonesController extends AppController {
} else {
return $this->flash(__('The zone could not be deleted. Please, try again.'), array('action' => 'index'));
}
- }}
+ }
+
+
+
+ public function createZoneImage( $id = null ) {
+ $this->loadModel('Monitor');
+ $this->Monitor->id = $id;
+ if (!$this->Monitor->exists()) {
+ throw new NotFoundException(__('Invalid zone'));
+ }
+
+
+ $this->loadModel('Config');
+ $zm_dir_images = $this->Config->find('list', array(
+ 'conditions' => array('Name' => 'ZM_DIR_IMAGES'),
+ 'fields' => array('Name', 'Value')
+ ));
+
+ $zm_dir_images = $zm_dir_images['ZM_DIR_IMAGES'];
+ $zm_path_web = Configure::read('ZM_PATH_WEB');
+ $zm_path_bin = Configure::read('ZM_PATH_BIN');
+ $images_path = "$zm_path_web/$zm_dir_images";
+
+ chdir($images_path);
+
+ $command = escapeshellcmd("$zm_path_bin/zmu -z -m $id");
+ system( $command, $status );
+
+ $this->set(array(
+ 'status' => $status,
+ '_serialize' => array('status')
+ ));
+
+ }
+}
diff --git a/web/api/app/Model/Config.php b/web/api/app/Model/Config.php
index 2ada8258d..d83728c76 100644
--- a/web/api/app/Model/Config.php
+++ b/web/api/app/Model/Config.php
@@ -18,13 +18,25 @@ class Config extends AppModel {
*
* @var string
*/
- public $primaryKey = 'Id';
+ public $primaryKey = 'Name';
/**
* Display field
*
* @var string
*/
- public $displayField = 'Name';
+ public $displayField = 'Value';
+
+
+ // Add a find method for returning a hash of the Config table.
+ // This is used for the Options view.
+ public $findMethods = array('hash' => true);
+ protected function _findHash($state, $query, $results = array()) {
+ if ($state === 'before') {
+ return $query;
+ }
+ $results = Set::combine($results, '{n}.Config.Name', '{n}.Config');
+ return $results;
+ }
}
diff --git a/web/api/app/Model/Control.php b/web/api/app/Model/Control.php
new file mode 100644
index 000000000..d5716cc66
--- /dev/null
+++ b/web/api/app/Model/Control.php
@@ -0,0 +1,54 @@
+ array(
+ 'numeric' => array(
+ 'rule' => array('numeric'),
+ //'message' => 'Your custom message here',
+ //'allowEmpty' => false,
+ //'required' => false,
+ //'last' => false, // Stop validation after this rule
+ //'on' => 'create', // Limit validation to 'create' or 'update' operations
+ ),
+ ),
+ );
+
+}
diff --git a/web/api/app/Model/Frame.php b/web/api/app/Model/Frame.php
index 48b880304..96c5fdc50 100644
--- a/web/api/app/Model/Frame.php
+++ b/web/api/app/Model/Frame.php
@@ -121,4 +121,6 @@ class Frame extends AppModel {
'order' => ''
)
);
+
+ public $recursive = -1;
}
diff --git a/web/api/app/Model/Host.php b/web/api/app/Model/Host.php
new file mode 100644
index 000000000..5c24c7531
--- /dev/null
+++ b/web/api/app/Model/Host.php
@@ -0,0 +1,9 @@
+
+Paginator->params();
+ echo json_encode($array);
+?>
diff --git a/web/api/app/View/Logs/json/index.ctp b/web/api/app/View/Logs/json/index.ctp
new file mode 100644
index 000000000..32cbd51f8
--- /dev/null
+++ b/web/api/app/View/Logs/json/index.ctp
@@ -0,0 +1,5 @@
+Paginator->params();
+ echo json_encode($array);
+?>
diff --git a/web/api/app/View/View/Configs/json/edit.ctp b/web/api/app/View/View/Configs/json/edit.ctp
new file mode 100644
index 000000000..75fa758bd
--- /dev/null
+++ b/web/api/app/View/View/Configs/json/edit.ctp
@@ -0,0 +1 @@
+echo json_encode($config);
diff --git a/web/api/app/View/View/Configs/json/index.ctp b/web/api/app/View/View/Configs/json/index.ctp
new file mode 100644
index 000000000..86edf870a
--- /dev/null
+++ b/web/api/app/View/View/Configs/json/index.ctp
@@ -0,0 +1 @@
+echo json_encode($configs);
diff --git a/web/api/app/View/View/Configs/json/view.ctp b/web/api/app/View/View/Configs/json/view.ctp
new file mode 100644
index 000000000..75fa758bd
--- /dev/null
+++ b/web/api/app/View/View/Configs/json/view.ctp
@@ -0,0 +1 @@
+echo json_encode($config);
diff --git a/web/api/app/View/View/Configs/xml/index.ctp b/web/api/app/View/View/Configs/xml/index.ctp
new file mode 100644
index 000000000..b13a76093
--- /dev/null
+++ b/web/api/app/View/View/Configs/xml/index.ctp
@@ -0,0 +1,2 @@
+$xml = Xml::fromArray(array('response' => $configs));
+echo $xml->asXML();
diff --git a/web/api/app/View/View/Configs/xml/view.ctp b/web/api/app/View/View/Configs/xml/view.ctp
new file mode 100644
index 000000000..7987d32e7
--- /dev/null
+++ b/web/api/app/View/View/Configs/xml/view.ctp
@@ -0,0 +1,2 @@
+$xml = Xml::fromArray(array('response' => $config));
+echo $xml->asXML();
diff --git a/web/api/app/View/View/Elements/empty b/web/api/app/View/View/Elements/empty
new file mode 100644
index 000000000..e69de29bb
diff --git a/web/api/app/View/View/Emails/html/default.ctp b/web/api/app/View/View/Emails/html/default.ctp
new file mode 100644
index 000000000..e2bff19c0
--- /dev/null
+++ b/web/api/app/View/View/Emails/html/default.ctp
@@ -0,0 +1,25 @@
+
+ ' . $line . "
\n";
+endforeach;
+?>
\ No newline at end of file
diff --git a/web/api/app/View/View/Emails/text/default.ctp b/web/api/app/View/View/Emails/text/default.ctp
new file mode 100644
index 000000000..090b5c403
--- /dev/null
+++ b/web/api/app/View/View/Emails/text/default.ctp
@@ -0,0 +1,19 @@
+
+
\ No newline at end of file
diff --git a/web/api/app/View/View/Errors/error400.ctp b/web/api/app/View/View/Errors/error400.ctp
new file mode 100644
index 000000000..4c3850b28
--- /dev/null
+++ b/web/api/app/View/View/Errors/error400.ctp
@@ -0,0 +1,31 @@
+
+
+
+=')):
+ echo '';
+ echo __d('cake_dev', 'Your version of PHP is 5.2.8 or higher.');
+ echo '';
+ else:
+ echo '';
+ echo __d('cake_dev', 'Your version of PHP is too low. You need PHP 5.2.8 or higher to use CakePHP.');
+ echo '';
+ endif;
+?>
+
+ ';
+ echo __d('cake_dev', 'The %s is being used for core caching. To change the config edit %s', ''. $settings['engine'] . 'Engine', 'APP/Config/core.php');
+ echo '';
+ else:
+ echo '';
+ echo __d('cake_dev', 'Your cache is NOT working. Please check the settings in %s', 'APP/Config/core.php');
+ echo '';
+ endif;
+ ?>
+
+ isConnected()):
+ echo '';
+ echo __d('cake_dev', 'CakePHP is able to connect to the database.');
+ echo '';
+ else:
+ echo '';
+ echo __d('cake_dev', 'CakePHP is NOT able to connect to the database.');
+ echo '
';
+ echo $errorMsg;
+ echo '';
+ endif;
+ ?>
+
+
+';
+ echo __d('cake_dev', 'PCRE has not been compiled with Unicode support.');
+ echo ' ';
+ echo __d('cake_dev', 'Recompile PCRE with Unicode support by adding --enable-unicode-properties when configuring');
+ echo '';
+ endif;
+?>
+
+
+ ';
+ echo __d('cake_dev', 'DebugKit plugin is present');
+ echo '';
+ else:
+ echo '';
+ echo __d('cake_dev', 'DebugKit is not installed. It will help you inspect and debug different aspects of your application.');
+ echo ' ';
+ echo __d('cake_dev', 'You can install it from %s', $this->Html->link('GitHub', 'https://github.com/cakephp/debug_kit'));
+ echo '';
+ endif;
+ ?>
+
+
+
+
+
+To change its layout, edit: %s.
+You can also add some CSS styles for your pages at: %s.',
+ 'APP/View/Pages/home.ctp', 'APP/View/Layouts/default.ctp', 'APP/webroot/css');
+?>
+
diff --git a/web/api/app/View/View/Scaffolds/empty b/web/api/app/View/View/Scaffolds/empty
new file mode 100644
index 000000000..e69de29bb
diff --git a/web/api/app/vendor/autoload.php b/web/api/app/vendor/autoload.php
new file mode 100644
index 000000000..0a2797817
--- /dev/null
+++ b/web/api/app/vendor/autoload.php
@@ -0,0 +1,7 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0 class loader
+ *
+ * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
+ *
+ * $loader = new \Composer\Autoload\ClassLoader();
+ *
+ * // register classes with namespaces
+ * $loader->add('Symfony\Component', __DIR__.'/component');
+ * $loader->add('Symfony', __DIR__.'/framework');
+ *
+ * // activate the autoloader
+ * $loader->register();
+ *
+ * // to enable searching the include path (eg. for PEAR packages)
+ * $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier
+ * @author Jordi Boggiano
+ */
+class ClassLoader
+{
+ // PSR-4
+ private $prefixLengthsPsr4 = array();
+ private $prefixDirsPsr4 = array();
+ private $fallbackDirsPsr4 = array();
+
+ // PSR-0
+ private $prefixesPsr0 = array();
+ private $fallbackDirsPsr0 = array();
+
+ private $useIncludePath = false;
+ private $classMap = array();
+
+ private $classMapAuthoritative = false;
+
+ public function getPrefixes()
+ {
+ if (!empty($this->prefixesPsr0)) {
+ return call_user_func_array('array_merge', $this->prefixesPsr0);
+ }
+
+ return array();
+ }
+
+ public function getPrefixesPsr4()
+ {
+ return $this->prefixDirsPsr4;
+ }
+
+ public function getFallbackDirs()
+ {
+ return $this->fallbackDirsPsr0;
+ }
+
+ public function getFallbackDirsPsr4()
+ {
+ return $this->fallbackDirsPsr4;
+ }
+
+ public function getClassMap()
+ {
+ return $this->classMap;
+ }
+
+ /**
+ * @param array $classMap Class to filename map
+ */
+ public function addClassMap(array $classMap)
+ {
+ if ($this->classMap) {
+ $this->classMap = array_merge($this->classMap, $classMap);
+ } else {
+ $this->classMap = $classMap;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix, either
+ * appending or prepending to the ones previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param array|string $paths The PSR-0 root directories
+ * @param bool $prepend Whether to prepend the directories
+ */
+ public function add($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ if ($prepend) {
+ $this->fallbackDirsPsr0 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr0
+ );
+ } else {
+ $this->fallbackDirsPsr0 = array_merge(
+ $this->fallbackDirsPsr0,
+ (array) $paths
+ );
+ }
+
+ return;
+ }
+
+ $first = $prefix[0];
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+ return;
+ }
+ if ($prepend) {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixesPsr0[$first][$prefix]
+ );
+ } else {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $this->prefixesPsr0[$first][$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace, either
+ * appending or prepending to the ones previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param array|string $paths The PSR-0 base directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function addPsr4($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ // Register directories for the root namespace.
+ if ($prepend) {
+ $this->fallbackDirsPsr4 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr4
+ );
+ } else {
+ $this->fallbackDirsPsr4 = array_merge(
+ $this->fallbackDirsPsr4,
+ (array) $paths
+ );
+ }
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+ // Register directories for a new namespace.
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ } elseif ($prepend) {
+ // Prepend directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixDirsPsr4[$prefix]
+ );
+ } else {
+ // Append directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $this->prefixDirsPsr4[$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix,
+ * replacing any others previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param array|string $paths The PSR-0 base directories
+ */
+ public function set($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr0 = (array) $paths;
+ } else {
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace,
+ * replacing any others previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param array|string $paths The PSR-4 base directories
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setPsr4($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr4 = (array) $paths;
+ } else {
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Turns on searching the include path for class files.
+ *
+ * @param bool $useIncludePath
+ */
+ public function setUseIncludePath($useIncludePath)
+ {
+ $this->useIncludePath = $useIncludePath;
+ }
+
+ /**
+ * Can be used to check if the autoloader uses the include path to check
+ * for classes.
+ *
+ * @return bool
+ */
+ public function getUseIncludePath()
+ {
+ return $this->useIncludePath;
+ }
+
+ /**
+ * Turns off searching the prefix and fallback directories for classes
+ * that have not been registered with the class map.
+ *
+ * @param bool $classMapAuthoritative
+ */
+ public function setClassMapAuthoritative($classMapAuthoritative)
+ {
+ $this->classMapAuthoritative = $classMapAuthoritative;
+ }
+
+ /**
+ * Should class lookup fail if not found in the current class map?
+ *
+ * @return bool
+ */
+ public function isClassMapAuthoritative()
+ {
+ return $this->classMapAuthoritative;
+ }
+
+ /**
+ * Registers this instance as an autoloader.
+ *
+ * @param bool $prepend Whether to prepend the autoloader or not
+ */
+ public function register($prepend = false)
+ {
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+ }
+
+ /**
+ * Unregisters this instance as an autoloader.
+ */
+ public function unregister()
+ {
+ spl_autoload_unregister(array($this, 'loadClass'));
+ }
+
+ /**
+ * Loads the given class or interface.
+ *
+ * @param string $class The name of the class
+ * @return bool|null True if loaded, null otherwise
+ */
+ public function loadClass($class)
+ {
+ if ($file = $this->findFile($class)) {
+ includeFile($file);
+
+ return true;
+ }
+ }
+
+ /**
+ * Finds the path to the file where the class is defined.
+ *
+ * @param string $class The name of the class
+ *
+ * @return string|false The path if found, false otherwise
+ */
+ public function findFile($class)
+ {
+ // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
+ if ('\\' == $class[0]) {
+ $class = substr($class, 1);
+ }
+
+ // class map lookup
+ if (isset($this->classMap[$class])) {
+ return $this->classMap[$class];
+ }
+ if ($this->classMapAuthoritative) {
+ return false;
+ }
+
+ $file = $this->findFileWithExtension($class, '.php');
+
+ // Search for Hack files if we are running on HHVM
+ if ($file === null && defined('HHVM_VERSION')) {
+ $file = $this->findFileWithExtension($class, '.hh');
+ }
+
+ if ($file === null) {
+ // Remember that this class does not exist.
+ return $this->classMap[$class] = false;
+ }
+
+ return $file;
+ }
+
+ private function findFileWithExtension($class, $ext)
+ {
+ // PSR-4 lookup
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+ $first = $class[0];
+ if (isset($this->prefixLengthsPsr4[$first])) {
+ foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
+ if (is_file($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-4 fallback dirs
+ foreach ($this->fallbackDirsPsr4 as $dir) {
+ if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 lookup
+ if (false !== $pos = strrpos($class, '\\')) {
+ // namespaced class name
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+ } else {
+ // PEAR-like class name
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+ }
+
+ if (isset($this->prefixesPsr0[$first])) {
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($dirs as $dir) {
+ if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-0 fallback dirs
+ foreach ($this->fallbackDirsPsr0 as $dir) {
+ if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 include paths.
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+ return $file;
+ }
+ }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ */
+function includeFile($file)
+{
+ include $file;
+}
diff --git a/web/api/app/vendor/composer/autoload_classmap.php b/web/api/app/vendor/composer/autoload_classmap.php
new file mode 100644
index 000000000..7a91153b0
--- /dev/null
+++ b/web/api/app/vendor/composer/autoload_classmap.php
@@ -0,0 +1,9 @@
+ array($vendorDir . '/composer/installers/src'),
+);
diff --git a/web/api/app/vendor/composer/autoload_psr4.php b/web/api/app/vendor/composer/autoload_psr4.php
new file mode 100644
index 000000000..b265c64a2
--- /dev/null
+++ b/web/api/app/vendor/composer/autoload_psr4.php
@@ -0,0 +1,9 @@
+ $path) {
+ $loader->set($namespace, $path);
+ }
+
+ $map = require __DIR__ . '/autoload_psr4.php';
+ foreach ($map as $namespace => $path) {
+ $loader->setPsr4($namespace, $path);
+ }
+
+ $classMap = require __DIR__ . '/autoload_classmap.php';
+ if ($classMap) {
+ $loader->addClassMap($classMap);
+ }
+
+ $loader->register(true);
+
+ return $loader;
+ }
+}
+
+function composerRequiredfd8518a66bb8898e7b22470609a6c8f($file)
+{
+ require $file;
+}
diff --git a/web/api/app/vendor/composer/installed.json b/web/api/app/vendor/composer/installed.json
new file mode 100644
index 000000000..39d771174
--- /dev/null
+++ b/web/api/app/vendor/composer/installed.json
@@ -0,0 +1,167 @@
+[
+ {
+ "name": "composer/installers",
+ "version": "v1.0.21",
+ "version_normalized": "1.0.21.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/installers.git",
+ "reference": "d64e23fce42a4063d63262b19b8e7c0f3b5e4c45"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/installers/zipball/d64e23fce42a4063d63262b19b8e7c0f3b5e4c45",
+ "reference": "d64e23fce42a4063d63262b19b8e7c0f3b5e4c45",
+ "shasum": ""
+ },
+ "replace": {
+ "roundcube/plugin-installer": "*",
+ "shama/baton": "*"
+ },
+ "require-dev": {
+ "composer/composer": "1.0.*@dev",
+ "phpunit/phpunit": "4.1.*"
+ },
+ "time": "2015-02-18 17:17:01",
+ "type": "composer-installer",
+ "extra": {
+ "class": "Composer\\Installers\\Installer",
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-0": {
+ "Composer\\Installers\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Kyle Robinson Young",
+ "email": "kyle@dontkry.com",
+ "homepage": "https://github.com/shama"
+ }
+ ],
+ "description": "A multi-framework Composer library installer",
+ "homepage": "http://composer.github.com/installers/",
+ "keywords": [
+ "Craft",
+ "Dolibarr",
+ "Hurad",
+ "MODX Evo",
+ "OXID",
+ "SMF",
+ "Thelia",
+ "WolfCMS",
+ "agl",
+ "aimeos",
+ "annotatecms",
+ "bitrix",
+ "cakephp",
+ "chef",
+ "codeigniter",
+ "concrete5",
+ "croogo",
+ "dokuwiki",
+ "drupal",
+ "elgg",
+ "fuelphp",
+ "grav",
+ "installer",
+ "joomla",
+ "kohana",
+ "laravel",
+ "lithium",
+ "magento",
+ "mako",
+ "mediawiki",
+ "modulework",
+ "moodle",
+ "phpbb",
+ "piwik",
+ "ppi",
+ "puppet",
+ "roundcube",
+ "shopware",
+ "silverstripe",
+ "symfony",
+ "typo3",
+ "wordpress",
+ "zend",
+ "zikula"
+ ]
+ },
+ {
+ "name": "friendsofcake/crud",
+ "version": "3.0.10",
+ "version_normalized": "3.0.10.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/FriendsOfCake/crud.git",
+ "reference": "c3976f1478c681b0bbc132ec3a3e82c3984eeed5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/FriendsOfCake/crud/zipball/c3976f1478c681b0bbc132ec3a3e82c3984eeed5",
+ "reference": "c3976f1478c681b0bbc132ec3a3e82c3984eeed5",
+ "shasum": ""
+ },
+ "require": {
+ "composer/installers": "*"
+ },
+ "suggest": {
+ "cakedc/search": "If you want to use the Search Listener"
+ },
+ "time": "2015-04-18 19:08:17",
+ "type": "cakephp-plugin",
+ "extra": {
+ "installer-name": "Crud"
+ },
+ "installation-source": "dist",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Winther",
+ "homepage": "http://cakephp.nu/",
+ "role": "Author"
+ },
+ {
+ "name": "José Lorenzo Rodríguez",
+ "homepage": "https://github.com/lorenzo",
+ "role": "Contributor"
+ },
+ {
+ "name": "Andy Dawson",
+ "homepage": "https://github.com/ad7six",
+ "role": "Contributor"
+ },
+ {
+ "name": "ADmad",
+ "homepage": "https://github.com/admad",
+ "role": "Contributor"
+ }
+ ],
+ "description": "CakePHP Application development on steroids - rapid prototyping / scaffolding & production ready code - XML / JSON APIs and more",
+ "homepage": "https://github.com/FriendsOfCake/crud",
+ "keywords": [
+ "bake",
+ "cake",
+ "cakephp",
+ "create",
+ "crud",
+ "delete",
+ "retrieve",
+ "scaffold",
+ "scaffolding",
+ "update"
+ ]
+ }
+]
diff --git a/web/api/app/vendor/composer/installers/.editorconfig b/web/api/app/vendor/composer/installers/.editorconfig
new file mode 100644
index 000000000..153cf3ef5
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/.editorconfig
@@ -0,0 +1,10 @@
+; top-most EditorConfig file
+root = true
+
+; Unix-style newlines
+[*]
+end_of_line = LF
+
+[*.php]
+indent_style = space
+indent_size = 4
diff --git a/web/api/app/vendor/composer/installers/.gitignore b/web/api/app/vendor/composer/installers/.gitignore
new file mode 100644
index 000000000..ff7f293dc
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/.gitignore
@@ -0,0 +1,3 @@
+vendor/
+composer.lock
+.idea/
diff --git a/web/api/app/vendor/composer/installers/.travis.yml b/web/api/app/vendor/composer/installers/.travis.yml
new file mode 100644
index 000000000..81ca8e101
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/.travis.yml
@@ -0,0 +1,14 @@
+language: php
+
+php:
+ - 5.3
+ - 5.4
+ - 5.5
+ - 5.6
+ - hhvm
+
+before_script:
+ - curl -s http://getcomposer.org/installer | php -- --quiet
+ - php composer.phar install --dev
+
+script: phpunit
diff --git a/web/api/app/vendor/composer/installers/LICENSE b/web/api/app/vendor/composer/installers/LICENSE
new file mode 100644
index 000000000..85f97fc79
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2012 Kyle Robinson Young
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/web/api/app/vendor/composer/installers/README.md b/web/api/app/vendor/composer/installers/README.md
new file mode 100644
index 000000000..b33177198
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/README.md
@@ -0,0 +1,191 @@
+# A Multi-Framework [Composer](http://getcomposer.org) Library Installer
+
+[![Build Status](http://img.shields.io/travis/composer/installers.svg)](http://travis-ci.org/composer/installers)
+
+This is for PHP package authors to require in their `composer.json`. It will
+install their package to the correct location based on the specified package
+type.
+
+The goal of `installers` is to be a simple package type to install path map.
+Users can also customize the install path per package and package authors can
+modify the package name upon installing.
+
+`installers` isn't intended on replacing all custom installers. If your
+package requires special installation handling then by all means, create a
+custom installer to handle it.
+
+**Natively Supported Frameworks**:
+
+The following frameworks natively work with Composer and will be
+installed to the default `vendor` directory. `composer/installers`
+is not needed to install packages with these frameworks:
+
+* Aura
+* Symfony2
+* Yii
+* Yii2
+
+**Current Supported Package Types**:
+
+> Stable types are marked as **bold**, this means that installation paths
+> for those type will not be changed. Any adjustment for those types would
+> require creation of brand new type that will cover required changes.
+
+| Framework | Types
+| --------- | -----
+| Aimeos | `aimeos-extension`
+| Asgard | `asgard-module` `asgard-theme`
+| AGL | `agl-module`
+| AnnotateCms | `annotatecms-module` `annotatecms-component` `annotatecms-service`
+| Bitrix | `bitrix-module` `bitrix-component` `bitrix-theme`
+| CakePHP 2+ | **`cakephp-plugin`**
+| Chef | `chef-cookbook` `chef-role`
+| CCFramework | `ccframework-ship` `ccframework-theme`
+| CodeIgniter | `codeigniter-library` `codeigniter-third-party` `codeigniter-module`
+| concrete5 | `concrete5-block` `concrete5-package` `concrete5-theme` `concrete5-update`
+| Craft | `craft-plugin`
+| Croogo | `croogo-plugin` `croogo-theme`
+| DokuWiki | `dokuwiki-plugin` `dokuwiki-template`
+| Dolibarr | `dolibarr-module`
+| Drupal | `drupal-module` `drupal-theme` `drupal-library` `drupal-profile` `drupal-drush`
+| Elgg | `elgg-plugin`
+| FuelPHP v1.x | `fuel-module` `fuel-package` `fuel-theme`
+| FuelPHP v2.x | `fuelphp-component`
+| Grav | `grav-plugin` `grav-theme`
+| Hurad | `hurad-plugin` `hurad-theme`
+| Joomla | `joomla-component` `joomla-module` `joomla-template` `joomla-plugin` `joomla-library`
+| Kirby | **`kirby-plugin`**
+| Kohana | **`kohana-module`**
+| Laravel | `laravel-library`
+| Lithium | **`lithium-library` `lithium-source`**
+| Magento | `magento-library` `magento-skin` `magento-theme`
+| Mako | `mako-package`
+| MODX Evo | `modxevo-snippet` `modxevo-plugin` `modxevo-module` `modxevo-template` `modxevo-lib`
+| MediaWiki | `mediawiki-extension`
+| October | **`october-module` `october-plugin` `october-theme`**
+| OXID | `oxid-module` `oxid-theme` `oxid-out`
+| MODULEWork | `modulework-module`
+| Moodle | `moodle-*` (Please [check source](https://raw.githubusercontent.com/composer/installers/master/src/Composer/Installers/MoodleInstaller.php) for all supported types)
+| Piwik | `piwik-plugin`
+| phpBB | `phpbb-extension` `phpbb-style` `phpbb-language`
+| Pimcore | `pimcore-plugin`
+| PPI | **`ppi-module`**
+| Puppet | `puppet-module`
+| REDAXO | `redaxo-addon`
+| Roundcube | `roundcube-plugin`
+| shopware | `shopware-backend-plugin` `shopware-core-plugin` `shopware-frontend-plugin` `shopware-theme`
+| SilverStripe | `silverstripe-module` `silverstripe-theme`
+| SMF | `smf-module` `smf-theme`
+| symfony1 | **`symfony1-plugin`**
+| Tusk | `tusk-task` `tusk-command` `tusk-asset`
+| TYPO3 Flow | `typo3-flow-package` `typo3-flow-framework` `typo3-flow-plugin` `typo3-flow-site` `typo3-flow-boilerplate` `typo3-flow-build`
+| TYPO3 CMS | `typo3-cms-extension`
+| Wolf CMS | `wolfcms-plugin`
+| WordPress | `wordpress-plugin` `wordpress-theme` `wordpress-muplugin`
+| Zend | `zend-library` `zend-extra` `zend-module`
+| Zikula | `zikula-module` `zikula-theme`
+| Prestashop | `prestashop-module` `prestashop-theme`
+
+## Example `composer.json` File
+
+This is an example for a CakePHP plugin. The only important parts to set in your
+composer.json file are `"type": "cakephp-plugin"` which describes what your
+package is and `"require": { "composer/installers": "~1.0" }` which tells composer
+to load the custom installers.
+
+```json
+{
+ "name": "you/ftp",
+ "type": "cakephp-plugin",
+ "require": {
+ "composer/installers": "~1.0"
+ }
+}
+```
+
+This would install your package to the `Plugin/Ftp/` folder of a CakePHP app
+when a user runs `php composer.phar install`.
+
+So submit your packages to [packagist.org](http://packagist.org)!
+
+## Custom Install Paths
+
+If you are consuming a package that uses the `composer/installers` you can
+override the install path with the following extra in your `composer.json`:
+
+```json
+{
+ "extra": {
+ "installer-paths": {
+ "your/custom/path/{$name}/": ["shama/ftp", "vendor/package"]
+ }
+ }
+}
+```
+
+A package type can have a custom installation path with a `type:` prefix.
+
+``` json
+{
+ "extra": {
+ "installer-paths": {
+ "your/custom/path/{$name}/": ["type:wordpress-plugin"]
+ }
+ }
+}
+```
+
+This would use your custom path for each of the listed packages. The available
+variables to use in your paths are: `{$name}`, `{$vendor}`, `{$type}`.
+
+## Custom Install Names
+
+If you're a package author and need your package to be named differently when
+installed consider using the `installer-name` extra.
+
+For example you have a package named `shama/cakephp-ftp` with the type
+`cakephp-plugin`. Installing with `composer/installers` would install to the
+path `Plugin/CakephpFtp`. Due to the strict naming conventions, you as a
+package author actually need the package to be named and installed to
+`Plugin/Ftp`. Using the following config within your **package** `composer.json`
+will allow this:
+
+```json
+{
+ "name": "shama/cakephp-ftp",
+ "type": "cakephp-plugin",
+ "extra": {
+ "installer-name": "Ftp"
+ }
+}
+```
+
+Please note the name entered into `installer-name` will be the final and will
+not be inflected.
+
+## Contribute!
+
+* [Fork and clone](https://help.github.com/articles/fork-a-repo).
+* Run the command `php composer.phar install --dev` to install the dev
+ dependencies. See [Composer](https://github.com/composer/composer#installation--usage).
+* Use the command `phpunit` to run the tests. See [PHPUnit](http://phpunit.de).
+* Create a branch, commit, push and send us a
+ [pull request](https://help.github.com/articles/using-pull-requests).
+
+To ensure a consistent code base, you should make sure the code follows the
+[Coding Standards](http://symfony.com/doc/2.0/contributing/code/standards.html)
+which we borrowed from Symfony.
+
+If you would like to help, please take a look at the list of
+[issues](https://github.com/composer/installers/issues).
+
+### Should we allow dynamic package types or paths? No.
+What are they? The ability for a package author to determine where a package
+will be installed either through setting the path directly in their
+`composer.json` or through a dynamic package type: `"type":
+"framework-install-here"`.
+
+It has been proposed many times. Even implemented once early on and then
+removed. `installers` won't do this because it would allow a single package
+author to wipe out entire folders without the user's consent. That user would
+then come here to yell at us.
diff --git a/web/api/app/vendor/composer/installers/composer.json b/web/api/app/vendor/composer/installers/composer.json
new file mode 100644
index 000000000..6ee931ee9
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/composer.json
@@ -0,0 +1,77 @@
+{
+ "name": "composer/installers",
+ "type": "composer-installer",
+ "license": "MIT",
+ "description": "A multi-framework Composer library installer",
+ "keywords": [
+ "installer",
+ "Aimeos",
+ "AGL",
+ "AnnotateCms",
+ "Bitrix",
+ "CakePHP",
+ "Chef",
+ "CodeIgniter",
+ "concrete5",
+ "Craft",
+ "Croogo",
+ "DokuWiki",
+ "Dolibarr",
+ "Drupal",
+ "Elgg",
+ "FuelPHP",
+ "Grav",
+ "Hurad",
+ "Joomla",
+ "Kohana",
+ "Laravel",
+ "Lithium",
+ "Magento",
+ "Mako",
+ "MODX Evo",
+ "MediaWiki",
+ "OXID",
+ "MODULEWork",
+ "Moodle",
+ "Piwik",
+ "phpBB",
+ "PPI",
+ "Puppet",
+ "Roundcube",
+ "shopware",
+ "SilverStripe",
+ "SMF",
+ "symfony",
+ "Thelia",
+ "TYPO3",
+ "WolfCMS",
+ "WordPress",
+ "Zend",
+ "Zikula"
+ ],
+ "homepage": "http://composer.github.com/installers/",
+ "authors": [
+ {
+ "name": "Kyle Robinson Young",
+ "email": "kyle@dontkry.com",
+ "homepage": "https://github.com/shama"
+ }
+ ],
+ "autoload": {
+ "psr-0": { "Composer\\Installers\\": "src/" }
+ },
+ "extra": {
+ "class": "Composer\\Installers\\Installer",
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "replace": {
+ "shama/baton": "*",
+ "roundcube/plugin-installer": "*"
+ },
+ "require-dev": {
+ "composer/composer": "1.0.*@dev",
+ "phpunit/phpunit": "4.1.*"
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/phpunit.xml.dist b/web/api/app/vendor/composer/installers/phpunit.xml.dist
new file mode 100644
index 000000000..cc5cc9915
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/phpunit.xml.dist
@@ -0,0 +1,25 @@
+
+
+
+
+
+ tests/Composer/Installers
+
+
+
+
+
+ src/Composer/Installers
+
+
+
\ No newline at end of file
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/AglInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/AglInstaller.php
new file mode 100644
index 000000000..01b8a4165
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/AglInstaller.php
@@ -0,0 +1,21 @@
+ 'More/{$name}/',
+ );
+
+ /**
+ * Format package name to CamelCase
+ */
+ public function inflectPackageVars($vars)
+ {
+ $vars['name'] = preg_replace_callback('/(?:^|_|-)(.?)/', function ($matches) {
+ return strtoupper($matches[1]);
+ }, $vars['name']);
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/AimeosInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/AimeosInstaller.php
new file mode 100644
index 000000000..79a0e958f
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/AimeosInstaller.php
@@ -0,0 +1,9 @@
+ 'ext/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/AnnotateCmsInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/AnnotateCmsInstaller.php
new file mode 100644
index 000000000..89d7ad905
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/AnnotateCmsInstaller.php
@@ -0,0 +1,11 @@
+ 'addons/modules/{$name}/',
+ 'component' => 'addons/components/{$name}/',
+ 'service' => 'addons/services/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/AsgardInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/AsgardInstaller.php
new file mode 100644
index 000000000..995ee2b49
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/AsgardInstaller.php
@@ -0,0 +1,45 @@
+ 'Modules/{$name}/',
+ 'theme' => 'Themes/{$name}/'
+ );
+
+ /**
+ * Format package name.
+ *
+ * For package type asgard-module, cut off a trailing '-plugin' if present.
+ *
+ * For package type asgard-theme, cut off a trailing '-theme' if present.
+ *
+ */
+ public function inflectPackageVars($vars)
+ {
+ if ($vars['type'] === 'asgard-module') {
+ return $this->inflectPluginVars($vars);
+ }
+
+ if ($vars['type'] === 'asgard-theme') {
+ return $this->inflectThemeVars($vars);
+ }
+
+ return $vars;
+ }
+
+ protected function inflectPluginVars($vars)
+ {
+ $vars['name'] = ucfirst(preg_replace('/-module/', '', $vars['name']));
+
+ return $vars;
+ }
+
+ protected function inflectThemeVars($vars)
+ {
+ $vars['name'] = ucfirst(preg_replace('/-theme$/', '', $vars['name']));
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/BaseInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/BaseInstaller.php
new file mode 100644
index 000000000..cc27d3e28
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/BaseInstaller.php
@@ -0,0 +1,131 @@
+composer = $composer;
+ $this->package = $package;
+ }
+
+ /**
+ * Return the install path based on package type.
+ *
+ * @param PackageInterface $package
+ * @param string $frameworkType
+ * @return string
+ */
+ public function getInstallPath(PackageInterface $package, $frameworkType = '')
+ {
+ $type = $this->package->getType();
+
+ $prettyName = $this->package->getPrettyName();
+ if (strpos($prettyName, '/') !== false) {
+ list($vendor, $name) = explode('/', $prettyName);
+ } else {
+ $vendor = '';
+ $name = $prettyName;
+ }
+
+ $availableVars = $this->inflectPackageVars(compact('name', 'vendor', 'type'));
+
+ $extra = $package->getExtra();
+ if (!empty($extra['installer-name'])) {
+ $availableVars['name'] = $extra['installer-name'];
+ }
+
+ if ($this->composer->getPackage()) {
+ $extra = $this->composer->getPackage()->getExtra();
+ if (!empty($extra['installer-paths'])) {
+ $customPath = $this->mapCustomInstallPaths($extra['installer-paths'], $prettyName, $type);
+ if ($customPath !== false) {
+ return $this->templatePath($customPath, $availableVars);
+ }
+ }
+ }
+
+ $packageType = substr($type, strlen($frameworkType) + 1);
+ $locations = $this->getLocations();
+ if (!isset($locations[$packageType])) {
+ throw new \InvalidArgumentException(sprintf('Package type "%s" is not supported', $type));
+ }
+
+ return $this->templatePath($locations[$packageType], $availableVars);
+ }
+
+ /**
+ * For an installer to override to modify the vars per installer.
+ *
+ * @param array $vars
+ * @return array
+ */
+ public function inflectPackageVars($vars)
+ {
+ return $vars;
+ }
+
+ /**
+ * Gets the installer's locations
+ *
+ * @return array
+ */
+ public function getLocations()
+ {
+ return $this->locations;
+ }
+
+ /**
+ * Replace vars in a path
+ *
+ * @param string $path
+ * @param array $vars
+ * @return string
+ */
+ protected function templatePath($path, array $vars = array())
+ {
+ if (strpos($path, '{') !== false) {
+ extract($vars);
+ preg_match_all('@\{\$([A-Za-z0-9_]*)\}@i', $path, $matches);
+ if (!empty($matches[1])) {
+ foreach ($matches[1] as $var) {
+ $path = str_replace('{$' . $var . '}', $$var, $path);
+ }
+ }
+ }
+
+ return $path;
+ }
+
+ /**
+ * Search through a passed paths array for a custom install path.
+ *
+ * @param array $paths
+ * @param string $name
+ * @param string $type
+ * @return string
+ */
+ protected function mapCustomInstallPaths(array $paths, $name, $type)
+ {
+ foreach ($paths as $path => $names) {
+ if (in_array($name, $names) || in_array('type:' . $type, $names)) {
+ return $path;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/BitrixInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/BitrixInstaller.php
new file mode 100644
index 000000000..48a8367ab
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/BitrixInstaller.php
@@ -0,0 +1,11 @@
+ 'local/modules/{$name}/',
+ 'component' => 'local/components/{$name}/',
+ 'theme' => 'local/templates/{$name}/'
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/CakePHPInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/CakePHPInstaller.php
new file mode 100644
index 000000000..cbeb60b80
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/CakePHPInstaller.php
@@ -0,0 +1,78 @@
+ 'Plugin/{$name}/',
+ );
+
+ /**
+ * Format package name to CamelCase
+ */
+ public function inflectPackageVars($vars)
+ {
+ if ($this->matchesCakeVersion('>=', '3.0.0')) {
+ return $vars;
+ }
+
+ $nameParts = explode('/', $vars['name']);
+ foreach ($nameParts as &$value) {
+ $value = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $value));
+ $value = str_replace(array('-', '_'), ' ', $value);
+ $value = str_replace(' ', '', ucwords($value));
+ }
+ $vars['name'] = implode('/', $nameParts);
+
+ return $vars;
+ }
+
+ /**
+ * Change the default plugin location when cakephp >= 3.0
+ */
+ public function getLocations()
+ {
+ if ($this->matchesCakeVersion('>=', '3.0.0')) {
+ $this->locations['plugin'] = $this->composer->getConfig()->get('vendor-dir') . '/{$vendor}/{$name}/';
+ }
+ return $this->locations;
+ }
+
+ /**
+ * Check if CakePHP version matches against a version
+ *
+ * @param string $matcher
+ * @param string $version
+ * @return bool
+ */
+ protected function matchesCakeVersion($matcher, $version)
+ {
+ $repositoryManager = $this->composer->getRepositoryManager();
+ if ($repositoryManager) {
+ $repos = $repositoryManager->getLocalRepository();
+ if (!$repos) {
+ return false;
+ }
+ $cake3 = new MultiConstraint(array(
+ new VersionConstraint($matcher, $version),
+ new VersionConstraint('!=', '9999999-dev'),
+ ));
+ $pool = new Pool('dev');
+ $pool->addRepository($repos);
+ $packages = $pool->whatProvides('cakephp/cakephp');
+ foreach ($packages as $package) {
+ $installed = new VersionConstraint('=', $package->getVersion());
+ if ($cake3->matches($installed)) {
+ return true;
+ break;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/ChefInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/ChefInstaller.php
new file mode 100644
index 000000000..ab2f9aad8
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/ChefInstaller.php
@@ -0,0 +1,11 @@
+ 'Chef/{$vendor}/{$name}/',
+ 'role' => 'Chef/roles/{$name}/',
+ );
+}
+
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/ClanCatsFrameworkInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/ClanCatsFrameworkInstaller.php
new file mode 100644
index 000000000..c887815c9
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/ClanCatsFrameworkInstaller.php
@@ -0,0 +1,10 @@
+ 'CCF/orbit/{$name}/',
+ 'theme' => 'CCF/app/themes/{$name}/',
+ );
+}
\ No newline at end of file
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/CodeIgniterInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/CodeIgniterInstaller.php
new file mode 100644
index 000000000..3b4a4ece1
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/CodeIgniterInstaller.php
@@ -0,0 +1,11 @@
+ 'application/libraries/{$name}/',
+ 'third-party' => 'application/third_party/{$name}/',
+ 'module' => 'application/modules/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/Concrete5Installer.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/Concrete5Installer.php
new file mode 100644
index 000000000..4d398a445
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/Concrete5Installer.php
@@ -0,0 +1,12 @@
+ 'blocks/{$name}/',
+ 'package' => 'packages/{$name}/',
+ 'theme' => 'themes/{$name}/',
+ 'update' => 'updates/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/CraftInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/CraftInstaller.php
new file mode 100644
index 000000000..dc3be8d1a
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/CraftInstaller.php
@@ -0,0 +1,9 @@
+ 'craft/plugins/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/CroogoInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/CroogoInstaller.php
new file mode 100644
index 000000000..d94219d3a
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/CroogoInstaller.php
@@ -0,0 +1,21 @@
+ 'Plugin/{$name}/',
+ 'theme' => 'View/Themed/{$name}/',
+ );
+
+ /**
+ * Format package name to CamelCase
+ */
+ public function inflectPackageVars($vars)
+ {
+ $vars['name'] = strtolower(str_replace(array('-', '_'), ' ', $vars['name']));
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/DokuWikiInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/DokuWikiInstaller.php
new file mode 100644
index 000000000..cfd638d5f
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/DokuWikiInstaller.php
@@ -0,0 +1,50 @@
+ 'lib/plugins/{$name}/',
+ 'template' => 'lib/tpl/{$name}/',
+ );
+
+ /**
+ * Format package name.
+ *
+ * For package type dokuwiki-plugin, cut off a trailing '-plugin',
+ * or leading dokuwiki_ if present.
+ *
+ * For package type dokuwiki-template, cut off a trailing '-template' if present.
+ *
+ */
+ public function inflectPackageVars($vars)
+ {
+
+ if ($vars['type'] === 'dokuwiki-plugin') {
+ return $this->inflectPluginVars($vars);
+ }
+
+ if ($vars['type'] === 'dokuwiki-template') {
+ return $this->inflectTemplateVars($vars);
+ }
+
+ return $vars;
+ }
+
+ protected function inflectPluginVars($vars)
+ {
+ $vars['name'] = preg_replace('/-plugin$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/^dokuwiki_?-?/', '', $vars['name']);
+
+ return $vars;
+ }
+
+ protected function inflectTemplateVars($vars)
+ {
+ $vars['name'] = preg_replace('/-template$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/^dokuwiki_?-?/', '', $vars['name']);
+
+ return $vars;
+ }
+
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/DolibarrInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/DolibarrInstaller.php
new file mode 100644
index 000000000..21f7e8e80
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/DolibarrInstaller.php
@@ -0,0 +1,16 @@
+
+ */
+class DolibarrInstaller extends BaseInstaller
+{
+ //TODO: Add support for scripts and themes
+ protected $locations = array(
+ 'module' => 'htdocs/custom/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/DrupalInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/DrupalInstaller.php
new file mode 100644
index 000000000..179413145
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/DrupalInstaller.php
@@ -0,0 +1,14 @@
+ 'core/',
+ 'module' => 'modules/{$name}/',
+ 'theme' => 'themes/{$name}/',
+ 'library' => 'libraries/{$name}/',
+ 'profile' => 'profiles/{$name}/',
+ 'drush' => 'drush/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/ElggInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/ElggInstaller.php
new file mode 100644
index 000000000..c0bb609f4
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/ElggInstaller.php
@@ -0,0 +1,9 @@
+ 'mod/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/FuelInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/FuelInstaller.php
new file mode 100644
index 000000000..6eba2e34f
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/FuelInstaller.php
@@ -0,0 +1,11 @@
+ 'fuel/app/modules/{$name}/',
+ 'package' => 'fuel/packages/{$name}/',
+ 'theme' => 'fuel/app/themes/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/FuelphpInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/FuelphpInstaller.php
new file mode 100644
index 000000000..29d980b30
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/FuelphpInstaller.php
@@ -0,0 +1,9 @@
+ 'components/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/GravInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/GravInstaller.php
new file mode 100644
index 000000000..dbe63e07e
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/GravInstaller.php
@@ -0,0 +1,30 @@
+ 'user/plugins/{$name}/',
+ 'theme' => 'user/themes/{$name}/',
+ );
+
+ /**
+ * Format package name
+ *
+ * @param array $vars
+ *
+ * @return array
+ */
+ public function inflectPackageVars($vars)
+ {
+ $restrictedWords = implode('|', array_keys($this->locations));
+
+ $vars['name'] = strtolower($vars['name']);
+ $vars['name'] = preg_replace('/^(?:grav-)?(?:(?:'.$restrictedWords.')-)?(.*?)(?:-(?:'.$restrictedWords.'))?$/ui',
+ '$1',
+ $vars['name']
+ );
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/HuradInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/HuradInstaller.php
new file mode 100644
index 000000000..8fe017f0f
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/HuradInstaller.php
@@ -0,0 +1,25 @@
+ 'plugins/{$name}/',
+ 'theme' => 'plugins/{$name}/',
+ );
+
+ /**
+ * Format package name to CamelCase
+ */
+ public function inflectPackageVars($vars)
+ {
+ $nameParts = explode('/', $vars['name']);
+ foreach ($nameParts as &$value) {
+ $value = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $value));
+ $value = str_replace(array('-', '_'), ' ', $value);
+ $value = str_replace(' ', '', ucwords($value));
+ }
+ $vars['name'] = implode('/', $nameParts);
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/Installer.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/Installer.php
new file mode 100644
index 000000000..63ba64ce3
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/Installer.php
@@ -0,0 +1,163 @@
+ 'AimeosInstaller',
+ 'asgard' => 'AsgardInstaller',
+ 'agl' => 'AglInstaller',
+ 'annotatecms' => 'AnnotateCmsInstaller',
+ 'bitrix' => 'BitrixInstaller',
+ 'cakephp' => 'CakePHPInstaller',
+ 'chef' => 'ChefInstaller',
+ 'ccframework' => 'ClanCatsFrameworkInstaller',
+ 'codeigniter' => 'CodeIgniterInstaller',
+ 'concrete5' => 'Concrete5Installer',
+ 'craft' => 'CraftInstaller',
+ 'croogo' => 'CroogoInstaller',
+ 'dokuwiki' => 'DokuWikiInstaller',
+ 'dolibarr' => 'DolibarrInstaller',
+ 'drupal' => 'DrupalInstaller',
+ 'elgg' => 'ElggInstaller',
+ 'fuel' => 'FuelInstaller',
+ 'fuelphp' => 'FuelphpInstaller',
+ 'grav' => 'GravInstaller',
+ 'hurad' => 'HuradInstaller',
+ 'joomla' => 'JoomlaInstaller',
+ 'kirby' => 'KirbyInstaller',
+ 'kohana' => 'KohanaInstaller',
+ 'laravel' => 'LaravelInstaller',
+ 'lithium' => 'LithiumInstaller',
+ 'magento' => 'MagentoInstaller',
+ 'mako' => 'MakoInstaller',
+ 'mediawiki' => 'MediaWikiInstaller',
+ 'microweber' => 'MicroweberInstaller',
+ 'modulework' => 'MODULEWorkInstaller',
+ 'modxevo' => 'MODXEvoInstaller',
+ 'moodle' => 'MoodleInstaller',
+ 'october' => 'OctoberInstaller',
+ 'oxid' => 'OxidInstaller',
+ 'phpbb' => 'PhpBBInstaller',
+ 'pimcore' => 'PimcoreInstaller',
+ 'piwik' => 'PiwikInstaller',
+ 'ppi' => 'PPIInstaller',
+ 'puppet' => 'PuppetInstaller',
+ 'redaxo' => 'RedaxoInstaller',
+ 'roundcube' => 'RoundcubeInstaller',
+ 'shopware' => 'ShopwareInstaller',
+ 'silverstripe' => 'SilverStripeInstaller',
+ 'smf' => 'SMFInstaller',
+ 'symfony1' => 'Symfony1Installer',
+ 'thelia' => 'TheliaInstaller',
+ 'tusk' => 'TuskInstaller',
+ 'typo3-cms' => 'TYPO3CmsInstaller',
+ 'typo3-flow' => 'TYPO3FlowInstaller',
+ 'whmcs' => 'WHMCSInstaller',
+ 'wolfcms' => 'WolfCMSInstaller',
+ 'wordpress' => 'WordPressInstaller',
+ 'zend' => 'ZendInstaller',
+ 'zikula' => 'ZikulaInstaller',
+ 'prestashop' => 'PrestashopInstaller',
+ );
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getInstallPath(PackageInterface $package)
+ {
+ $type = $package->getType();
+ $frameworkType = $this->findFrameworkType($type);
+
+ if ($frameworkType === false) {
+ throw new \InvalidArgumentException(
+ 'Sorry the package type of this package is not yet supported.'
+ );
+ }
+
+ $class = 'Composer\\Installers\\' . $this->supportedTypes[$frameworkType];
+ $installer = new $class($package, $this->composer);
+
+ return $installer->getInstallPath($package, $frameworkType);
+ }
+
+ public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package)
+ {
+ if (!$repo->hasPackage($package)) {
+ throw new \InvalidArgumentException('Package is not installed: '.$package);
+ }
+
+ $repo->removePackage($package);
+
+ $installPath = $this->getInstallPath($package);
+ $this->io->write(sprintf('Deleting %s - %s', $installPath, $this->filesystem->removeDirectory($installPath) ? 'deleted' : 'not deleted'));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function supports($packageType)
+ {
+ $frameworkType = $this->findFrameworkType($packageType);
+
+ if ($frameworkType === false) {
+ return false;
+ }
+
+ $locationPattern = $this->getLocationPattern($frameworkType);
+
+ return preg_match('#' . $frameworkType . '-' . $locationPattern . '#', $packageType, $matches) === 1;
+ }
+
+ /**
+ * Finds a supported framework type if it exists and returns it
+ *
+ * @param string $type
+ * @return string
+ */
+ protected function findFrameworkType($type)
+ {
+ $frameworkType = false;
+
+ krsort($this->supportedTypes);
+
+ foreach ($this->supportedTypes as $key => $val) {
+ if ($key === substr($type, 0, strlen($key))) {
+ $frameworkType = substr($type, 0, strlen($key));
+ break;
+ }
+ }
+
+ return $frameworkType;
+ }
+
+ /**
+ * Get the second part of the regular expression to check for support of a
+ * package type
+ *
+ * @param string $frameworkType
+ * @return string
+ */
+ protected function getLocationPattern($frameworkType)
+ {
+ $pattern = false;
+ if (!empty($this->supportedTypes[$frameworkType])) {
+ $frameworkClass = 'Composer\\Installers\\' . $this->supportedTypes[$frameworkType];
+ /** @var BaseInstaller $framework */
+ $framework = new $frameworkClass(null, $this->composer);
+ $locations = array_keys($framework->getLocations());
+ $pattern = $locations ? '(' . implode('|', $locations) . ')' : false;
+ }
+
+ return $pattern ? : '(\w+)';
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/JoomlaInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/JoomlaInstaller.php
new file mode 100644
index 000000000..9ee775965
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/JoomlaInstaller.php
@@ -0,0 +1,15 @@
+ 'components/{$name}/',
+ 'module' => 'modules/{$name}/',
+ 'template' => 'templates/{$name}/',
+ 'plugin' => 'plugins/{$name}/',
+ 'library' => 'libraries/{$name}/',
+ );
+
+ // TODO: Add inflector for mod_ and com_ names
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/KirbyInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/KirbyInstaller.php
new file mode 100644
index 000000000..ae7ba8a4b
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/KirbyInstaller.php
@@ -0,0 +1,9 @@
+ 'site/plugins/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/KohanaInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/KohanaInstaller.php
new file mode 100644
index 000000000..dcd6d2632
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/KohanaInstaller.php
@@ -0,0 +1,9 @@
+ 'modules/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/LaravelInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/LaravelInstaller.php
new file mode 100644
index 000000000..be4d53a7b
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/LaravelInstaller.php
@@ -0,0 +1,9 @@
+ 'libraries/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/LithiumInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/LithiumInstaller.php
new file mode 100644
index 000000000..47bbd4cab
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/LithiumInstaller.php
@@ -0,0 +1,10 @@
+ 'libraries/{$name}/',
+ 'source' => 'libraries/_source/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/MODULEWorkInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/MODULEWorkInstaller.php
new file mode 100644
index 000000000..9c2e9fb40
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/MODULEWorkInstaller.php
@@ -0,0 +1,9 @@
+ 'modules/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/MODXEvoInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/MODXEvoInstaller.php
new file mode 100644
index 000000000..5a664608d
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/MODXEvoInstaller.php
@@ -0,0 +1,16 @@
+ 'assets/snippets/{$name}/',
+ 'plugin' => 'assets/plugins/{$name}/',
+ 'module' => 'assets/modules/{$name}/',
+ 'template' => 'assets/templates/{$name}/',
+ 'lib' => 'assets/lib/{$name}/'
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/MagentoInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/MagentoInstaller.php
new file mode 100644
index 000000000..cf18e9478
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/MagentoInstaller.php
@@ -0,0 +1,11 @@
+ 'app/design/frontend/{$name}/',
+ 'skin' => 'skin/frontend/default/{$name}/',
+ 'library' => 'lib/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/MakoInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/MakoInstaller.php
new file mode 100644
index 000000000..ca3cfacb4
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/MakoInstaller.php
@@ -0,0 +1,9 @@
+ 'app/packages/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/MediaWikiInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/MediaWikiInstaller.php
new file mode 100644
index 000000000..01008c638
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/MediaWikiInstaller.php
@@ -0,0 +1,50 @@
+ 'extensions/{$name}/',
+ 'skin' => 'skins/{$name}/',
+ );
+
+ /**
+ * Format package name.
+ *
+ * For package type mediawiki-extension, cut off a trailing '-extension' if present and transform
+ * to CamelCase keeping existing uppercase chars.
+ *
+ * For package type mediawiki-skin, cut off a trailing '-skin' if present.
+ *
+ */
+ public function inflectPackageVars($vars)
+ {
+
+ if ($vars['type'] === 'mediawiki-extension') {
+ return $this->inflectExtensionVars($vars);
+ }
+
+ if ($vars['type'] === 'mediawiki-skin') {
+ return $this->inflectSkinVars($vars);
+ }
+
+ return $vars;
+ }
+
+ protected function inflectExtensionVars($vars)
+ {
+ $vars['name'] = preg_replace('/-extension$/', '', $vars['name']);
+ $vars['name'] = str_replace('-', ' ', $vars['name']);
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
+
+ return $vars;
+ }
+
+ protected function inflectSkinVars($vars)
+ {
+ $vars['name'] = preg_replace('/-skin$/', '', $vars['name']);
+
+ return $vars;
+ }
+
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/MicroweberInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/MicroweberInstaller.php
new file mode 100644
index 000000000..4bbbec8c0
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/MicroweberInstaller.php
@@ -0,0 +1,111 @@
+ 'userfiles/modules/{$name}/',
+ 'module-skin' => 'userfiles/modules/{$name}/templates/',
+ 'template' => 'userfiles/templates/{$name}/',
+ 'element' => 'userfiles/elements/{$name}/',
+ 'vendor' => 'vendor/{$name}/',
+ 'components' => 'components/{$name}/'
+ );
+
+ /**
+ * Format package name.
+ *
+ * For package type microweber-module, cut off a trailing '-module' if present
+ *
+ * For package type microweber-template, cut off a trailing '-template' if present.
+ *
+ */
+ public function inflectPackageVars($vars)
+ {
+ if ($vars['type'] === 'microweber-template') {
+ return $this->inflectTemplateVars($vars);
+ }
+ if ($vars['type'] === 'microweber-templates') {
+ return $this->inflectTemplatesVars($vars);
+ }
+ if ($vars['type'] === 'microweber-core') {
+ return $this->inflectCoreVars($vars);
+ }
+ if ($vars['type'] === 'microweber-adapter') {
+ return $this->inflectCoreVars($vars);
+ }
+ if ($vars['type'] === 'microweber-module') {
+ return $this->inflectModuleVars($vars);
+ }
+ if ($vars['type'] === 'microweber-modules') {
+ return $this->inflectModulesVars($vars);
+ }
+ if ($vars['type'] === 'microweber-skin') {
+ return $this->inflectSkinVars($vars);
+ }
+ if ($vars['type'] === 'microweber-element' or $vars['type'] === 'microweber-elements') {
+ return $this->inflectElementVars($vars);
+ }
+
+ return $vars;
+ }
+
+ protected function inflectTemplateVars($vars)
+ {
+ $vars['name'] = preg_replace('/-template$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/template-$/', '', $vars['name']);
+
+ return $vars;
+ }
+
+ protected function inflectTemplatesVars($vars)
+ {
+ $vars['name'] = preg_replace('/-templates$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/templates-$/', '', $vars['name']);
+
+ return $vars;
+ }
+
+ protected function inflectCoreVars($vars)
+ {
+ $vars['name'] = preg_replace('/-providers$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/-provider$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/-adapter$/', '', $vars['name']);
+
+ return $vars;
+ }
+
+ protected function inflectModuleVars($vars)
+ {
+ $vars['name'] = preg_replace('/-module$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/module-$/', '', $vars['name']);
+
+ return $vars;
+ }
+
+ protected function inflectModulesVars($vars)
+ {
+ $vars['name'] = preg_replace('/-modules$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/modules-$/', '', $vars['name']);
+
+ return $vars;
+ }
+
+ protected function inflectSkinVars($vars)
+ {
+ $vars['name'] = preg_replace('/-skin$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/skin-$/', '', $vars['name']);
+
+ return $vars;
+ }
+
+ protected function inflectElementVars($vars)
+ {
+ $vars['name'] = preg_replace('/-elements$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/elements-$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/-element$/', '', $vars['name']);
+ $vars['name'] = preg_replace('/element-$/', '', $vars['name']);
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/MoodleInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/MoodleInstaller.php
new file mode 100644
index 000000000..04be73c2a
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/MoodleInstaller.php
@@ -0,0 +1,47 @@
+ 'mod/{$name}/',
+ 'admin_report' => 'admin/report/{$name}/',
+ 'tool' => 'admin/tool/{$name}/',
+ 'assignment' => 'mod/assignment/type/{$name}/',
+ 'assignsubmission' => 'mod/assign/submission/{$name}/',
+ 'assignfeedback' => 'mod/assign/feedback/{$name}/',
+ 'auth' => 'auth/{$name}/',
+ 'availability' => 'availability/condition/{$name}/',
+ 'block' => 'blocks/{$name}/',
+ 'calendartype' => 'calendar/type/{$name}/',
+ 'format' => 'course/format/{$name}/',
+ 'coursereport' => 'course/report/{$name}/',
+ 'datafield' => 'mod/data/field/{$name}/',
+ 'datapreset' => 'mod/data/preset/{$name}/',
+ 'editor' => 'lib/editor/{$name}/',
+ 'enrol' => 'enrol/{$name}/',
+ 'filter' => 'filter/{$name}/',
+ 'gradeexport' => 'grade/export/{$name}/',
+ 'gradeimport' => 'grade/import/{$name}/',
+ 'gradereport' => 'grade/report/{$name}/',
+ 'gradingform' => 'grade/grading/form/{$name}/',
+ 'local' => 'local/{$name}/',
+ 'message' => 'message/output/{$name}/',
+ 'plagiarism' => 'plagiarism/{$name}/',
+ 'portfolio' => 'portfolio/{$name}/',
+ 'qbehaviour' => 'question/behaviour/{$name}/',
+ 'qformat' => 'question/format/{$name}/',
+ 'qtype' => 'question/type/{$name}/',
+ 'quizaccess' => 'mod/quiz/accessrule/{$name}/',
+ 'quiz' => 'mod/quiz/report/{$name}/',
+ 'report' => 'report/{$name}/',
+ 'repository' => 'repository/{$name}/',
+ 'scormreport' => 'mod/scorm/report/{$name}/',
+ 'theme' => 'theme/{$name}/',
+ 'profilefield' => 'user/profile/field/{$name}/',
+ 'webservice' => 'webservice/{$name}/',
+ 'workshopallocation' => 'mod/workshop/allocation/{$name}/',
+ 'workshopeval' => 'mod/workshop/eval/{$name}/',
+ 'workshopform' => 'mod/workshop/form/{$name}/'
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/OctoberInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/OctoberInstaller.php
new file mode 100644
index 000000000..6bf53fd14
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/OctoberInstaller.php
@@ -0,0 +1,46 @@
+ 'modules/{$name}/',
+ 'plugin' => 'plugins/{$vendor}/{$name}/',
+ 'theme' => 'themes/{$name}/'
+ );
+
+ /**
+ * Format package name.
+ *
+ * For package type october-plugin, cut off a trailing '-plugin' if present.
+ *
+ * For package type october-theme, cut off a trailing '-theme' if present.
+ *
+ */
+ public function inflectPackageVars($vars)
+ {
+ if ($vars['type'] === 'october-plugin') {
+ return $this->inflectPluginVars($vars);
+ }
+
+ if ($vars['type'] === 'october-theme') {
+ return $this->inflectThemeVars($vars);
+ }
+
+ return $vars;
+ }
+
+ protected function inflectPluginVars($vars)
+ {
+ $vars['name'] = preg_replace('/-plugin$/', '', $vars['name']);
+
+ return $vars;
+ }
+
+ protected function inflectThemeVars($vars)
+ {
+ $vars['name'] = preg_replace('/-theme$/', '', $vars['name']);
+
+ return $vars;
+ }
+}
\ No newline at end of file
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/OxidInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/OxidInstaller.php
new file mode 100644
index 000000000..22fb56aa1
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/OxidInstaller.php
@@ -0,0 +1,11 @@
+ 'modules/{$name}/',
+ 'theme' => 'application/views/{$name}/',
+ 'out' => 'out/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/PPIInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/PPIInstaller.php
new file mode 100644
index 000000000..170136f98
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/PPIInstaller.php
@@ -0,0 +1,9 @@
+ 'modules/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/PhpBBInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/PhpBBInstaller.php
new file mode 100644
index 000000000..deb2b77a6
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/PhpBBInstaller.php
@@ -0,0 +1,11 @@
+ 'ext/{$vendor}/{$name}/',
+ 'language' => 'language/{$name}/',
+ 'style' => 'styles/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/PimcoreInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/PimcoreInstaller.php
new file mode 100644
index 000000000..4781fa6d1
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/PimcoreInstaller.php
@@ -0,0 +1,21 @@
+ 'plugins/{$name}/',
+ );
+
+ /**
+ * Format package name to CamelCase
+ */
+ public function inflectPackageVars($vars)
+ {
+ $vars['name'] = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $vars['name']));
+ $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']);
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/PiwikInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/PiwikInstaller.php
new file mode 100644
index 000000000..c17f4572b
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/PiwikInstaller.php
@@ -0,0 +1,32 @@
+ 'plugins/{$name}/',
+ );
+
+ /**
+ * Format package name to CamelCase
+ * @param array $vars
+ *
+ * @return array
+ */
+ public function inflectPackageVars($vars)
+ {
+ $vars['name'] = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $vars['name']));
+ $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']);
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/PrestashopInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/PrestashopInstaller.php
new file mode 100644
index 000000000..4c8421e36
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/PrestashopInstaller.php
@@ -0,0 +1,10 @@
+ 'modules/{$name}/',
+ 'theme' => 'themes/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/PuppetInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/PuppetInstaller.php
new file mode 100644
index 000000000..77cc3dd87
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/PuppetInstaller.php
@@ -0,0 +1,11 @@
+ 'modules/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/RedaxoInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/RedaxoInstaller.php
new file mode 100644
index 000000000..09544576b
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/RedaxoInstaller.php
@@ -0,0 +1,10 @@
+ 'redaxo/include/addons/{$name}/',
+ 'bestyle-plugin' => 'redaxo/include/addons/be_style/plugins/{$name}/'
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/RoundcubeInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/RoundcubeInstaller.php
new file mode 100644
index 000000000..d8d795be0
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/RoundcubeInstaller.php
@@ -0,0 +1,22 @@
+ 'plugins/{$name}/',
+ );
+
+ /**
+ * Lowercase name and changes the name to a underscores
+ *
+ * @param array $vars
+ * @return array
+ */
+ public function inflectPackageVars($vars)
+ {
+ $vars['name'] = strtolower(str_replace('-', '_', $vars['name']));
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/SMFInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/SMFInstaller.php
new file mode 100644
index 000000000..1acd3b14c
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/SMFInstaller.php
@@ -0,0 +1,10 @@
+ 'Sources/{$name}/',
+ 'theme' => 'Themes/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/ShopwareInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/ShopwareInstaller.php
new file mode 100644
index 000000000..673f1fc1f
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/ShopwareInstaller.php
@@ -0,0 +1,58 @@
+ 'engine/Shopware/Plugins/Local/Backend/{$name}/',
+ 'core-plugin' => 'engine/Shopware/Plugins/Local/Core/{$name}/',
+ 'frontend-plugin' => 'engine/Shopware/Plugins/Local/Frontend/{$name}/',
+ 'theme' => 'templates/{$name}/'
+ );
+
+ /**
+ * Transforms the names
+ * @param array $vars
+ * @return array
+ */
+ public function inflectPackageVars($vars)
+ {
+ if ($vars['type'] === 'shopware-theme') {
+ return $this->correctThemeName($vars);
+ } else {
+ return $this->correctPluginName($vars);
+ }
+ }
+
+ /**
+ * Changes the name to a camelcased combination of vendor and name
+ * @param array $vars
+ * @return array
+ */
+ private function correctPluginName($vars)
+ {
+ $camelCasedName = preg_replace_callback('/(-[a-z])/', function ($matches) {
+ return strtoupper($matches[0][1]);
+ }, $vars['name']);
+
+ $vars['name'] = ucfirst($vars['vendor']) . ucfirst($camelCasedName);
+
+ return $vars;
+ }
+
+ /**
+ * Changes the name to a underscore separated name
+ * @param array $vars
+ * @return array
+ */
+ private function correctThemeName($vars)
+ {
+ $vars['name'] = str_replace('-', '_', $vars['name']);
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/SilverStripeInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/SilverStripeInstaller.php
new file mode 100644
index 000000000..17ca543a2
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/SilverStripeInstaller.php
@@ -0,0 +1,36 @@
+ '{$name}/',
+ 'theme' => 'themes/{$name}/',
+ );
+
+ /**
+ * Return the install path based on package type.
+ *
+ * Relies on built-in BaseInstaller behaviour with one exception: silverstripe/framework
+ * must be installed to 'sapphire' and not 'framework' if the version is <3.0.0
+ *
+ * @param PackageInterface $package
+ * @param string $frameworkType
+ * @return string
+ */
+ public function getInstallPath(PackageInterface $package, $frameworkType = '')
+ {
+ if (
+ $package->getName() == 'silverstripe/framework'
+ && preg_match('/^\d+\.\d+\.\d+/', $package->getVersion())
+ && version_compare($package->getVersion(), '2.999.999') < 0
+ ) {
+ return $this->templatePath($this->locations['module'], array('name' => 'sapphire'));
+ } else {
+ return parent::getInstallPath($package, $frameworkType);
+ }
+
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/Symfony1Installer.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/Symfony1Installer.php
new file mode 100644
index 000000000..1675c4f21
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/Symfony1Installer.php
@@ -0,0 +1,26 @@
+
+ */
+class Symfony1Installer extends BaseInstaller
+{
+ protected $locations = array(
+ 'plugin' => 'plugins/{$name}/',
+ );
+
+ /**
+ * Format package name to CamelCase
+ */
+ public function inflectPackageVars($vars)
+ {
+ $vars['name'] = preg_replace_callback('/(-[a-z])/', function ($matches) {
+ return strtoupper($matches[0][1]);
+ }, $vars['name']);
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/TYPO3CmsInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/TYPO3CmsInstaller.php
new file mode 100644
index 000000000..8220b40df
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/TYPO3CmsInstaller.php
@@ -0,0 +1,14 @@
+
+ */
+class TYPO3CmsInstaller extends BaseInstaller
+{
+ protected $locations = array(
+ 'extension' => 'typo3conf/ext/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/TYPO3FlowInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/TYPO3FlowInstaller.php
new file mode 100644
index 000000000..42572f44f
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/TYPO3FlowInstaller.php
@@ -0,0 +1,38 @@
+ 'Packages/Application/{$name}/',
+ 'framework' => 'Packages/Framework/{$name}/',
+ 'plugin' => 'Packages/Plugins/{$name}/',
+ 'site' => 'Packages/Sites/{$name}/',
+ 'boilerplate' => 'Packages/Boilerplates/{$name}/',
+ 'build' => 'Build/{$name}/',
+ );
+
+ /**
+ * Modify the package name to be a TYPO3 Flow style key.
+ *
+ * @param array $vars
+ * @return array
+ */
+ public function inflectPackageVars($vars)
+ {
+ $autoload = $this->package->getAutoload();
+ if (isset($autoload['psr-0']) && is_array($autoload['psr-0'])) {
+ $namespace = key($autoload['psr-0']);
+ $vars['name'] = str_replace('\\', '.', $namespace);
+ }
+ if (isset($autoload['psr-4']) && is_array($autoload['psr-4'])) {
+ $namespace = key($autoload['psr-4']);
+ $vars['name'] = rtrim(str_replace('\\', '.', $namespace), '.');
+ }
+
+ return $vars;
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/TheliaInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/TheliaInstaller.php
new file mode 100644
index 000000000..158af5261
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/TheliaInstaller.php
@@ -0,0 +1,12 @@
+ 'local/modules/{$name}/',
+ 'frontoffice-template' => 'templates/frontOffice/{$name}/',
+ 'backoffice-template' => 'templates/backOffice/{$name}/',
+ 'email-template' => 'templates/email/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/TuskInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/TuskInstaller.php
new file mode 100644
index 000000000..7c0113b85
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/TuskInstaller.php
@@ -0,0 +1,14 @@
+
+ */
+ class TuskInstaller extends BaseInstaller
+ {
+ protected $locations = array(
+ 'task' => '.tusk/tasks/{$name}/',
+ 'command' => '.tusk/commands/{$name}/',
+ 'asset' => 'assets/tusk/{$name}/',
+ );
+ }
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/WHMCSInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/WHMCSInstaller.php
new file mode 100644
index 000000000..2cbb4a463
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/WHMCSInstaller.php
@@ -0,0 +1,10 @@
+ 'modules/gateways/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/WolfCMSInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/WolfCMSInstaller.php
new file mode 100644
index 000000000..cb387881d
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/WolfCMSInstaller.php
@@ -0,0 +1,9 @@
+ 'wolf/plugins/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/WordPressInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/WordPressInstaller.php
new file mode 100644
index 000000000..b03219c69
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/WordPressInstaller.php
@@ -0,0 +1,11 @@
+ 'wp-content/plugins/{$name}/',
+ 'theme' => 'wp-content/themes/{$name}/',
+ 'muplugin' => 'wp-content/mu-plugins/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/ZendInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/ZendInstaller.php
new file mode 100644
index 000000000..bde9bc8c8
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/ZendInstaller.php
@@ -0,0 +1,11 @@
+ 'library/{$name}/',
+ 'extra' => 'extras/library/{$name}/',
+ 'module' => 'module/{$name}/',
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/ZikulaInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/ZikulaInstaller.php
new file mode 100644
index 000000000..56cdf5da7
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/ZikulaInstaller.php
@@ -0,0 +1,10 @@
+ 'modules/{$vendor}-{$name}/',
+ 'theme' => 'themes/{$vendor}-{$name}/'
+ );
+}
diff --git a/web/api/app/vendor/composer/installers/src/bootstrap.php b/web/api/app/vendor/composer/installers/src/bootstrap.php
new file mode 100644
index 000000000..0de276ee2
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/src/bootstrap.php
@@ -0,0 +1,13 @@
+installer = new AsgardInstaller(
+ new Package('NyanCat', '4.2', '4.2'),
+ new Composer()
+ );
+ }
+
+ /**
+ * @dataProvider packageNameInflectionProvider
+ */
+ public function testInflectPackageVars($type, $name, $expected)
+ {
+ $this->assertEquals(
+ $this->installer->inflectPackageVars(array('name' => $name, 'type' => $type)),
+ array('name' => $expected, 'type' => $type)
+ );
+ }
+
+ public function packageNameInflectionProvider()
+ {
+ return array(
+ array(
+ 'asgard-module',
+ 'asgard-module',
+ 'Asgard'
+ ),
+ array(
+ 'asgard-module',
+ 'blog',
+ 'Blog'
+ ),
+ // tests that exactly one '-theme' is cut off
+ array(
+ 'asgard-theme',
+ 'some-theme-theme',
+ 'Some-theme',
+ ),
+ // tests that names without '-theme' suffix stay valid
+ array(
+ 'asgard-theme',
+ 'someothertheme',
+ 'Someothertheme',
+ ),
+ );
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/CakePHPInstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/CakePHPInstallerTest.php
new file mode 100644
index 000000000..976bd9be8
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/CakePHPInstallerTest.php
@@ -0,0 +1,115 @@
+package = new Package('CamelCased', '1.0', '1.0');
+ $this->io = $this->getMock('Composer\IO\PackageInterface');
+ $this->composer = new Composer();
+ $this->composer->setConfig(new Config(false));
+ }
+
+ /**
+ * testInflectPackageVars
+ *
+ * @return void
+ */
+ public function testInflectPackageVars()
+ {
+ $installer = new CakePHPInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'CamelCased'));
+ $this->assertEquals($result, array('name' => 'CamelCased'));
+
+ $installer = new CakePHPInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'with-dash'));
+ $this->assertEquals($result, array('name' => 'WithDash'));
+
+ $installer = new CakePHPInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'with_underscore'));
+ $this->assertEquals($result, array('name' => 'WithUnderscore'));
+
+ $installer = new CakePHPInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'cake/acl'));
+ $this->assertEquals($result, array('name' => 'Cake/Acl'));
+
+ $installer = new CakePHPInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'cake/debug-kit'));
+ $this->assertEquals($result, array('name' => 'Cake/DebugKit'));
+ }
+
+ /**
+ * Test getLocations returning appropriate values based on CakePHP version
+ *
+ */
+ public function testGetLocations() {
+ $package = new RootPackage('CamelCased', '1.0', '1.0');
+ $composer = $this->composer;
+ $rm = new RepositoryManager(
+ $this->getMock('Composer\IO\IOInterface'),
+ $this->getMock('Composer\Config')
+ );
+ $composer->setRepositoryManager($rm);
+ $installer = new CakePHPInstaller($package, $composer);
+
+ // 2.0 < cakephp < 3.0
+ $this->setCakephpVersion($rm, '2.0.0');
+ $result = $installer->getLocations();
+ $this->assertContains('Plugin/', $result['plugin']);
+
+ $this->setCakephpVersion($rm, '2.5.9');
+ $result = $installer->getLocations();
+ $this->assertContains('Plugin/', $result['plugin']);
+
+ $this->setCakephpVersion($rm, '~2.5');
+ $result = $installer->getLocations();
+ $this->assertContains('Plugin/', $result['plugin']);
+
+ // special handling for 2.x versions when 3.x is still in development
+ $this->setCakephpVersion($rm, 'dev-master');
+ $result = $installer->getLocations();
+ $this->assertContains('Plugin/', $result['plugin']);
+
+ $this->setCakephpVersion($rm, '>=2.5');
+ $result = $installer->getLocations();
+ $this->assertContains('Plugin/', $result['plugin']);
+
+ // cakephp >= 3.0
+ $this->setCakephpVersion($rm, '3.0.*-dev');
+ $result = $installer->getLocations();
+ $this->assertContains('vendor/{$vendor}/{$name}/', $result['plugin']);
+
+ $this->setCakephpVersion($rm, '~8.8');
+ $result = $installer->getLocations();
+ $this->assertEquals('vendor/{$vendor}/{$name}/', $result['plugin']);
+ }
+
+ protected function setCakephpVersion($rm, $version) {
+ $parser = new VersionParser();
+ list(, $version) = explode(' ', $parser->parseConstraints($version));
+ $installed = new InstalledArrayRepository();
+ $package = new Package('cakephp/cakephp', $version, $version);
+ $installed->addPackage($package);
+ $rm->setLocalRepository($installed);
+ }
+
+}
diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/DokuWikiInstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/DokuWikiInstallerTest.php
new file mode 100644
index 000000000..9e385e6a8
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/DokuWikiInstallerTest.php
@@ -0,0 +1,89 @@
+installer = new DokuWikiInstaller(
+ new Package('NyanCat', '4.2', '4.2'),
+ new Composer()
+ );
+ }
+
+ /**
+ * @dataProvider packageNameInflectionProvider
+ */
+ public function testInflectPackageVars($type, $name, $expected)
+ {
+ $this->assertEquals(
+ $this->installer->inflectPackageVars(array('name' => $name, 'type'=>$type)),
+ array('name' => $expected, 'type'=>$type)
+ );
+ }
+
+ public function packageNameInflectionProvider()
+ {
+ return array(
+ array(
+ 'dokuwiki-plugin',
+ 'dokuwiki-test-plugin',
+ 'test',
+ ),
+ array(
+ 'dokuwiki-plugin',
+ 'test-plugin',
+ 'test',
+ ),
+ array(
+ 'dokuwiki-plugin',
+ 'dokuwiki_test',
+ 'test',
+ ),
+ array(
+ 'dokuwiki-plugin',
+ 'test',
+ 'test',
+ ),
+ array(
+ 'dokuwiki-plugin',
+ 'test-template',
+ 'test-template',
+ ),
+ array(
+ 'dokuwiki-template',
+ 'dokuwiki-test-template',
+ 'test',
+ ),
+ array(
+ 'dokuwiki-template',
+ 'test-template',
+ 'test',
+ ),
+ array(
+ 'dokuwiki-template',
+ 'dokuwiki_test',
+ 'test',
+ ),
+ array(
+ 'dokuwiki-template',
+ 'test',
+ 'test',
+ ),
+ array(
+ 'dokuwiki-template',
+ 'test-plugin',
+ 'test-plugin',
+ ),
+ );
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/GravInstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/GravInstallerTest.php
new file mode 100644
index 000000000..b757799b4
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/GravInstallerTest.php
@@ -0,0 +1,63 @@
+composer = new Composer();
+ }
+
+ public function testInflectPackageVars()
+ {
+ $package = $this->getPackage('vendor/name', '0.0.0');
+ $installer = new GravInstaller($package, $this->composer);
+ $packageVars = $this->getPackageVars($package);
+
+ $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => 'test')));
+ $this->assertEquals('test', $result['name']);
+
+ foreach ($installer->getLocations() as $name => $location) {
+ $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "$name-test")));
+ $this->assertEquals('test', $result['name']);
+ $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "test-$name")));
+ $this->assertEquals('test', $result['name']);
+ $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "$name-test-test")));
+ $this->assertEquals('test-test', $result['name']);
+ $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "test-test-$name")));
+ $this->assertEquals('test-test', $result['name']);
+ $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "grav-$name-test")));
+ $this->assertEquals('test', $result['name']);
+ $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "grav-test-$name")));
+ $this->assertEquals('test', $result['name']);
+ $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "grav-$name-test-test")));
+ $this->assertEquals('test-test', $result['name']);
+ $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "grav-test-test-$name")));
+ $this->assertEquals('test-test', $result['name']);
+ }
+ }
+
+ /**
+ * @param $package \Composer\Package\PackageInterface
+ */
+ public function getPackageVars($package)
+ {
+ $type = $package->getType();
+
+ $prettyName = $package->getPrettyName();
+ if (strpos($prettyName, '/') !== false) {
+ list($vendor, $name) = explode('/', $prettyName);
+ } else {
+ $vendor = '';
+ $name = $prettyName;
+ }
+
+ return compact('name', 'vendor', 'type');
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/InstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/InstallerTest.php
new file mode 100644
index 000000000..a516daf07
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/InstallerTest.php
@@ -0,0 +1,422 @@
+fs = new Filesystem;
+
+ $this->composer = new Composer();
+ $this->config = new Config();
+ $this->composer->setConfig($this->config);
+
+ $this->vendorDir = realpath(sys_get_temp_dir()) . DIRECTORY_SEPARATOR . 'baton-test-vendor';
+ $this->ensureDirectoryExistsAndClear($this->vendorDir);
+
+ $this->binDir = realpath(sys_get_temp_dir()) . DIRECTORY_SEPARATOR . 'baton-test-bin';
+ $this->ensureDirectoryExistsAndClear($this->binDir);
+
+ $this->config->merge(array(
+ 'config' => array(
+ 'vendor-dir' => $this->vendorDir,
+ 'bin-dir' => $this->binDir,
+ ),
+ ));
+
+ $this->dm = $this->getMockBuilder('Composer\Downloader\DownloadManager')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->composer->setDownloadManager($this->dm);
+
+ $this->repository = $this->getMock('Composer\Repository\InstalledRepositoryInterface');
+ $this->io = $this->getMock('Composer\IO\IOInterface');
+ }
+
+ /**
+ * tearDown
+ *
+ * @return void
+ */
+ public function tearDown()
+ {
+ $this->fs->removeDirectory($this->vendorDir);
+ $this->fs->removeDirectory($this->binDir);
+ }
+
+ /**
+ * testSupports
+ *
+ * @return void
+ *
+ * @dataProvider dataForTestSupport
+ */
+ public function testSupports($type, $expected)
+ {
+ $installer = new Installer($this->io, $this->composer);
+ $this->assertSame($expected, $installer->supports($type), sprintf('Failed to show support for %s', $type));
+ }
+
+ /**
+ * dataForTestSupport
+ */
+ public function dataForTestSupport()
+ {
+ return array(
+ array('agl-module', true),
+ array('aimeos-extension', true),
+ array('annotatecms-module', true),
+ array('annotatecms-component', true),
+ array('annotatecms-service', true),
+ array('bitrix-module', true),
+ array('bitrix-component', true),
+ array('bitrix-theme', true),
+ array('cakephp', false),
+ array('cakephp-', false),
+ array('cakephp-app', false),
+ array('cakephp-plugin', true),
+ array('chef-cookbook', true),
+ array('chef-role', true),
+ array('codeigniter-app', false),
+ array('codeigniter-library', true),
+ array('codeigniter-third-party', true),
+ array('codeigniter-module', true),
+ array('concrete5-block', true),
+ array('concrete5-package', true),
+ array('concrete5-theme', true),
+ array('concrete5-update', true),
+ array('craft-plugin', true),
+ array('croogo-plugin', true),
+ array('croogo-theme', true),
+ array('dokuwiki-plugin', true),
+ array('dokuwiki-template', true),
+ array('drupal-module', true),
+ array('dolibarr-module', true),
+ array('elgg-plugin', true),
+ array('fuel-module', true),
+ array('fuel-package', true),
+ array('fuel-theme', true),
+ array('fuelphp-component', true),
+ array('hurad-plugin', true),
+ array('hurad-theme', true),
+ array('joomla-library', true),
+ array('kirby-plugin', true),
+ array('kohana-module', true),
+ array('laravel-library', true),
+ array('lithium-library', true),
+ array('magento-library', true),
+ array('mako-package', true),
+ array('modxevo-snippet', true),
+ array('modxevo-plugin', true),
+ array('modxevo-module', true),
+ array('modxevo-template', true),
+ array('modxevo-lib', true),
+ array('mediawiki-extension', true),
+ array('mediawiki-skin', true),
+ array('microweber-module', true),
+ array('modulework-module', true),
+ array('moodle-mod', true),
+ array('october-module', true),
+ array('october-plugin', true),
+ array('piwik-plugin', true),
+ array('phpbb-extension', true),
+ array('pimcore-plugin', true),
+ array('ppi-module', true),
+ array('prestashop-module', true),
+ array('prestashop-theme', true),
+ array('puppet-module', true),
+ array('redaxo-addon', true),
+ array('redaxo-bestyle-plugin', true),
+ array('roundcube-plugin', true),
+ array('shopware-backend-plugin', true),
+ array('shopware-core-plugin', true),
+ array('shopware-frontend-plugin', true),
+ array('shopware-theme', true),
+ array('silverstripe-module', true),
+ array('silverstripe-theme', true),
+ array('smf-module', true),
+ array('smf-theme', true),
+ array('symfony1-plugin', true),
+ array('thelia-module', true),
+ array('thelia-frontoffice-template', true),
+ array('thelia-backoffice-template', true),
+ array('thelia-email-template', true),
+ array('tusk-task', true),
+ array('tusk-asset', true),
+ array('typo3-flow-plugin', true),
+ array('typo3-cms-extension', true),
+ array('whmcs-gateway', true),
+ array('wolfcms-plugin', true),
+ array('wordpress-plugin', true),
+ array('wordpress-core', false),
+ array('zend-library', true),
+ array('zikula-module', true),
+ array('zikula-theme', true),
+ );
+ }
+
+ /**
+ * testInstallPath
+ *
+ * @dataProvider dataForTestInstallPath
+ */
+ public function testInstallPath($type, $path, $name, $version = '1.0.0')
+ {
+ $installer = new Installer($this->io, $this->composer);
+ $package = new Package($name, $version, $version);
+
+ $package->setType($type);
+ $result = $installer->getInstallPath($package);
+ $this->assertEquals($path, $result);
+ }
+
+ /**
+ * dataFormTestInstallPath
+ */
+ public function dataForTestInstallPath()
+ {
+ return array(
+ array('agl-module', 'More/MyTestPackage/', 'agl/my_test-package'),
+ array('aimeos-extension', 'ext/ai-test/', 'author/ai-test'),
+ array('annotatecms-module', 'addons/modules/my_module/', 'vysinsky/my_module'),
+ array('annotatecms-component', 'addons/components/my_component/', 'vysinsky/my_component'),
+ array('annotatecms-service', 'addons/services/my_service/', 'vysinsky/my_service'),
+ array('bitrix-module', 'local/modules/my_module/', 'author/my_module'),
+ array('bitrix-component', 'local/components/my_component/', 'author/my_component'),
+ array('bitrix-theme', 'local/templates/my_theme/', 'author/my_theme'),
+ array('cakephp-plugin', 'Plugin/Ftp/', 'shama/ftp'),
+ array('chef-cookbook', 'Chef/mre/my_cookbook/', 'mre/my_cookbook'),
+ array('chef-role', 'Chef/roles/my_role/', 'mre/my_role'),
+ array('codeigniter-library', 'application/libraries/my_package/', 'shama/my_package'),
+ array('codeigniter-module', 'application/modules/my_package/', 'shama/my_package'),
+ array('concrete5-block', 'blocks/concrete5_block/', 'remo/concrete5_block'),
+ array('concrete5-package', 'packages/concrete5_package/', 'remo/concrete5_package'),
+ array('concrete5-theme', 'themes/concrete5_theme/', 'remo/concrete5_theme'),
+ array('concrete5-update', 'updates/concrete5/', 'concrete5/concrete5'),
+ array('craft-plugin', 'craft/plugins/my_plugin/', 'mdcpepper/my_plugin'),
+ array('croogo-plugin', 'Plugin/Sitemaps/', 'fahad19/sitemaps'),
+ array('croogo-theme', 'View/Themed/Readable/', 'rchavik/readable'),
+ array('dokuwiki-plugin', 'lib/plugins/someplugin/', 'author/someplugin'),
+ array('dokuwiki-template', 'lib/tpl/sometemplate/', 'author/sometemplate'),
+ array('dolibarr-module', 'htdocs/custom/my_module/', 'shama/my_module'),
+ array('drupal-module', 'modules/my_module/', 'shama/my_module'),
+ array('drupal-theme', 'themes/my_module/', 'shama/my_module'),
+ array('drupal-profile', 'profiles/my_module/', 'shama/my_module'),
+ array('drupal-drush', 'drush/my_module/', 'shama/my_module'),
+ array('elgg-plugin', 'mod/sample_plugin/', 'test/sample_plugin'),
+ array('fuel-module', 'fuel/app/modules/module/', 'fuel/module'),
+ array('fuel-package', 'fuel/packages/orm/', 'fuel/orm'),
+ array('fuel-theme', 'fuel/app/themes/theme/', 'fuel/theme'),
+ array('fuelphp-component', 'components/demo/', 'fuelphp/demo'),
+ array('hurad-plugin', 'plugins/Akismet/', 'atkrad/akismet'),
+ array('hurad-theme', 'plugins/Hurad2013/', 'atkrad/Hurad2013'),
+ array('joomla-plugin', 'plugins/my_plugin/', 'shama/my_plugin'),
+ array('kirby-plugin', 'site/plugins/my_plugin/', 'shama/my_plugin'),
+ array('kohana-module', 'modules/my_package/', 'shama/my_package'),
+ array('laravel-library', 'libraries/my_package/', 'shama/my_package'),
+ array('lithium-library', 'libraries/li3_test/', 'user/li3_test'),
+ array('magento-library', 'lib/foo/', 'test/foo'),
+ array('modxevo-snippet', 'assets/snippets/my_snippet/', 'shama/my_snippet'),
+ array('modxevo-plugin', 'assets/plugins/my_plugin/', 'shama/my_plugin'),
+ array('modxevo-module', 'assets/modules/my_module/', 'shama/my_module'),
+ array('modxevo-template', 'assets/templates/my_template/', 'shama/my_template'),
+ array('modxevo-lib', 'assets/lib/my_lib/', 'shama/my_lib'),
+ array('mako-package', 'app/packages/my_package/', 'shama/my_package'),
+ array('mediawiki-extension', 'extensions/APC/', 'author/APC'),
+ array('mediawiki-extension', 'extensions/APC/', 'author/APC-extension'),
+ array('mediawiki-extension', 'extensions/UploadWizard/', 'author/upload-wizard'),
+ array('mediawiki-extension', 'extensions/SyntaxHighlight_GeSHi/', 'author/syntax-highlight_GeSHi'),
+ array('mediawiki-skin', 'skins/someskin/', 'author/someskin-skin'),
+ array('mediawiki-skin', 'skins/someskin/', 'author/someskin'),
+ array('microweber-module', 'userfiles/modules/my-thing/', 'author/my-thing-module'),
+ array('modulework-module', 'modules/my_package/', 'shama/my_package'),
+ array('moodle-mod', 'mod/my_package/', 'shama/my_package'),
+ array('october-module', 'modules/my_plugin/', 'shama/my_plugin'),
+ array('october-plugin', 'plugins/shama/my_plugin/', 'shama/my_plugin'),
+ array('october-theme', 'themes/my_theme/', 'shama/my_theme'),
+ array('piwik-plugin', 'plugins/VisitSummary/', 'shama/visit-summary'),
+ array('prestashop-module', 'modules/a-module/', 'vendor/a-module'),
+ array('prestashop-theme', 'themes/a-theme/', 'vendor/a-theme'),
+ array('phpbb-extension', 'ext/test/foo/', 'test/foo'),
+ array('phpbb-style', 'styles/foo/', 'test/foo'),
+ array('phpbb-language', 'language/foo/', 'test/foo'),
+ array('pimcore-plugin', 'plugins/MyPlugin/', 'ubikz/my_plugin'),
+ array('ppi-module', 'modules/foo/', 'test/foo'),
+ array('puppet-module', 'modules/puppet-name/', 'puppet/puppet-name'),
+ array('redaxo-addon', 'redaxo/include/addons/my_plugin/', 'shama/my_plugin'),
+ array('redaxo-bestyle-plugin', 'redaxo/include/addons/be_style/plugins/my_plugin/', 'shama/my_plugin'),
+ array('roundcube-plugin', 'plugins/base/', 'test/base'),
+ array('roundcube-plugin', 'plugins/replace_dash/', 'test/replace-dash'),
+ array('shopware-backend-plugin', 'engine/Shopware/Plugins/Local/Backend/ShamaMyBackendPlugin/', 'shama/my-backend-plugin'),
+ array('shopware-core-plugin', 'engine/Shopware/Plugins/Local/Core/ShamaMyCorePlugin/', 'shama/my-core-plugin'),
+ array('shopware-frontend-plugin', 'engine/Shopware/Plugins/Local/Frontend/ShamaMyFrontendPlugin/', 'shama/my-frontend-plugin'),
+ array('shopware-theme', 'templates/my_theme/', 'shama/my-theme'),
+ array('silverstripe-module', 'my_module/', 'shama/my_module'),
+ array('silverstripe-module', 'sapphire/', 'silverstripe/framework', '2.4.0'),
+ array('silverstripe-module', 'framework/', 'silverstripe/framework', '3.0.0'),
+ array('silverstripe-module', 'framework/', 'silverstripe/framework', '3.0.0-rc1'),
+ array('silverstripe-module', 'framework/', 'silverstripe/framework', 'my/branch'),
+ array('silverstripe-theme', 'themes/my_theme/', 'shama/my_theme'),
+ array('smf-module', 'Sources/my_module/', 'shama/my_module'),
+ array('smf-theme', 'Themes/my_theme/', 'shama/my_theme'),
+ array('symfony1-plugin', 'plugins/sfShamaPlugin/', 'shama/sfShamaPlugin'),
+ array('symfony1-plugin', 'plugins/sfShamaPlugin/', 'shama/sf-shama-plugin'),
+ array('thelia-module', 'local/modules/my_module/', 'shama/my_module'),
+ array('thelia-frontoffice-template', 'templates/frontOffice/my_template_fo/', 'shama/my_template_fo'),
+ array('thelia-backoffice-template', 'templates/backOffice/my_template_bo/', 'shama/my_template_bo'),
+ array('thelia-email-template', 'templates/email/my_template_email/', 'shama/my_template_email'),
+ array('tusk-task', '.tusk/tasks/my_task/', 'shama/my_task'),
+ array('typo3-flow-package', 'Packages/Application/my_package/', 'shama/my_package'),
+ array('typo3-flow-build', 'Build/my_package/', 'shama/my_package'),
+ array('typo3-cms-extension', 'typo3conf/ext/my_extension/', 'shama/my_extension'),
+ array('whmcs-gateway', 'modules/gateways/gateway_name/', 'vendor/gateway_name'),
+ array('wolfcms-plugin', 'wolf/plugins/my_plugin/', 'shama/my_plugin'),
+ array('wordpress-plugin', 'wp-content/plugins/my_plugin/', 'shama/my_plugin'),
+ array('wordpress-muplugin', 'wp-content/mu-plugins/my_plugin/', 'shama/my_plugin'),
+ array('zend-extra', 'extras/library/zend_test/', 'shama/zend_test'),
+ array('zikula-module', 'modules/my-test_module/', 'my/test_module'),
+ array('zikula-theme', 'themes/my-test_theme/', 'my/test_theme'),
+ );
+ }
+
+ /**
+ * testGetCakePHPInstallPathException
+ *
+ * @return void
+ *
+ * @expectedException \InvalidArgumentException
+ */
+ public function testGetCakePHPInstallPathException()
+ {
+ $installer = new Installer($this->io, $this->composer);
+ $package = new Package('shama/ftp', '1.0.0', '1.0.0');
+
+ $package->setType('cakephp-whoops');
+ $result = $installer->getInstallPath($package);
+ }
+
+ /**
+ * testCustomInstallPath
+ */
+ public function testCustomInstallPath()
+ {
+ $installer = new Installer($this->io, $this->composer);
+ $package = new Package('shama/ftp', '1.0.0', '1.0.0');
+ $package->setType('cakephp-plugin');
+ $consumerPackage = new RootPackage('foo/bar', '1.0.0', '1.0.0');
+ $this->composer->setPackage($consumerPackage);
+ $consumerPackage->setExtra(array(
+ 'installer-paths' => array(
+ 'my/custom/path/{$name}/' => array(
+ 'shama/ftp',
+ 'foo/bar',
+ ),
+ ),
+ ));
+ $result = $installer->getInstallPath($package);
+ $this->assertEquals('my/custom/path/Ftp/', $result);
+ }
+
+ /**
+ * testCustomInstallerName
+ */
+ public function testCustomInstallerName()
+ {
+ $installer = new Installer($this->io, $this->composer);
+ $package = new Package('shama/cakephp-ftp-plugin', '1.0.0', '1.0.0');
+ $package->setType('cakephp-plugin');
+ $package->setExtra(array(
+ 'installer-name' => 'FTP',
+ ));
+ $result = $installer->getInstallPath($package);
+ $this->assertEquals('Plugin/FTP/', $result);
+ }
+
+ /**
+ * testCustomTypePath
+ */
+ public function testCustomTypePath()
+ {
+ $installer = new Installer($this->io, $this->composer);
+ $package = new Package('slbmeh/my_plugin', '1.0.0', '1.0.0');
+ $package->setType('wordpress-plugin');
+ $consumerPackage = new RootPackage('foo/bar', '1.0.0', '1.0.0');
+ $this->composer->setPackage($consumerPackage);
+ $consumerPackage->setExtra(array(
+ 'installer-paths' => array(
+ 'my/custom/path/{$name}/' => array(
+ 'type:wordpress-plugin'
+ ),
+ ),
+ ));
+ $result = $installer->getInstallPath($package);
+ $this->assertEquals('my/custom/path/my_plugin/', $result);
+ }
+
+ /**
+ * testNoVendorName
+ */
+ public function testNoVendorName()
+ {
+ $installer = new Installer($this->io, $this->composer);
+ $package = new Package('sfPhpunitPlugin', '1.0.0', '1.0.0');
+
+ $package->setType('symfony1-plugin');
+ $result = $installer->getInstallPath($package);
+ $this->assertEquals('plugins/sfPhpunitPlugin/', $result);
+ }
+
+ /**
+ * testTypo3Inflection
+ */
+ public function testTypo3Inflection()
+ {
+ $installer = new Installer($this->io, $this->composer);
+ $package = new Package('typo3/fluid', '1.0.0', '1.0.0');
+
+ $package->setAutoload(array(
+ 'psr-0' => array(
+ 'TYPO3\\Fluid' => 'Classes',
+ ),
+ ));
+
+ $package->setType('typo3-flow-package');
+ $result = $installer->getInstallPath($package);
+ $this->assertEquals('Packages/Application/TYPO3.Fluid/', $result);
+ }
+
+ public function testUninstallAndDeletePackageFromLocalRepo()
+ {
+ $package = new Package('foo', '1.0.0', '1.0.0');
+
+ $installer = $this->getMock('Composer\Installers\Installer', array('getInstallPath'), array($this->io, $this->composer));
+ $installer->expects($this->once())->method('getInstallPath')->with($package)->will($this->returnValue(sys_get_temp_dir().'/foo'));
+
+ $repo = $this->getMock('Composer\Repository\InstalledRepositoryInterface');
+ $repo->expects($this->once())->method('hasPackage')->with($package)->will($this->returnValue(true));
+ $repo->expects($this->once())->method('removePackage')->with($package);
+
+ $installer->uninstall($repo, $package);
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/MediaWikiInstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/MediaWikiInstallerTest.php
new file mode 100644
index 000000000..3675e188b
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/MediaWikiInstallerTest.php
@@ -0,0 +1,66 @@
+installer = new MediaWikiInstaller(
+ new Package('NyanCat', '4.2', '4.2'),
+ new Composer()
+ );
+ }
+
+ /**
+ * @dataProvider packageNameInflectionProvider
+ */
+ public function testInflectPackageVars($type, $name, $expected)
+ {
+ $this->assertEquals(
+ $this->installer->inflectPackageVars(array('name' => $name, 'type'=>$type)),
+ array('name' => $expected, 'type'=>$type)
+ );
+ }
+
+ public function packageNameInflectionProvider()
+ {
+ return array(
+ array(
+ 'mediawiki-extension',
+ 'sub-page-list',
+ 'SubPageList',
+ ),
+ array(
+ 'mediawiki-extension',
+ 'sub-page-list-extension',
+ 'SubPageList',
+ ),
+ array(
+ 'mediawiki-extension',
+ 'semantic-mediawiki',
+ 'SemanticMediawiki',
+ ),
+ // tests that exactly one '-skin' is cut off, and that skins do not get ucwords treatment like extensions
+ array(
+ 'mediawiki-skin',
+ 'some-skin-skin',
+ 'some-skin',
+ ),
+ // tests that names without '-skin' suffix stay valid
+ array(
+ 'mediawiki-skin',
+ 'someotherskin',
+ 'someotherskin',
+ ),
+ );
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/OctoberInstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/OctoberInstallerTest.php
new file mode 100644
index 000000000..fd427cdc3
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/OctoberInstallerTest.php
@@ -0,0 +1,66 @@
+installer = new OctoberInstaller(
+ new Package('NyanCat', '4.2', '4.2'),
+ new Composer()
+ );
+ }
+
+ /**
+ * @dataProvider packageNameInflectionProvider
+ */
+ public function testInflectPackageVars($type, $name, $expected)
+ {
+ $this->assertEquals(
+ $this->installer->inflectPackageVars(array('name' => $name, 'type' => $type)),
+ array('name' => $expected, 'type' => $type)
+ );
+ }
+
+ public function packageNameInflectionProvider()
+ {
+ return array(
+ array(
+ 'october-plugin',
+ 'subpagelist',
+ 'subpagelist',
+ ),
+ array(
+ 'october-plugin',
+ 'subpagelist-plugin',
+ 'subpagelist',
+ ),
+ array(
+ 'october-plugin',
+ 'semanticoctober',
+ 'semanticoctober',
+ ),
+ // tests that exactly one '-theme' is cut off
+ array(
+ 'october-theme',
+ 'some-theme-theme',
+ 'some-theme',
+ ),
+ // tests that names without '-theme' suffix stay valid
+ array(
+ 'october-theme',
+ 'someothertheme',
+ 'someothertheme',
+ ),
+ );
+ }
+}
\ No newline at end of file
diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/PimcoreInstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/PimcoreInstallerTest.php
new file mode 100644
index 000000000..ea79374bf
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/PimcoreInstallerTest.php
@@ -0,0 +1,44 @@
+package = new Package('CamelCased', '1.0', '1.0');
+ $this->io = $this->getMock('Composer\IO\PackageInterface');
+ $this->composer = new Composer();
+ }
+
+ /**
+ * testInflectPackageVars
+ *
+ * @return void
+ */
+ public function testInflectPackageVars()
+ {
+ $installer = new PimcoreInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'CamelCased'));
+ $this->assertEquals($result, array('name' => 'CamelCased'));
+
+ $installer = new PimcoreInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'with-dash'));
+ $this->assertEquals($result, array('name' => 'WithDash'));
+
+ $installer = new PimcoreInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'with_underscore'));
+ $this->assertEquals($result, array('name' => 'WithUnderscore'));
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/PiwikInstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/PiwikInstallerTest.php
new file mode 100644
index 000000000..8d9ff3f82
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/PiwikInstallerTest.php
@@ -0,0 +1,63 @@
+package = new Package('VisitSummary', '1.0', '1.0');
+ $this->io = $this->getMock('Composer\IO\PackageInterface');
+ $this->composer = new Composer();
+ }
+
+ /**
+ * testInflectPackageVars
+ *
+ * @return void
+ */
+ public function testInflectPackageVars()
+ {
+ $installer = new PiwikInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'VisitSummary'));
+ $this->assertEquals($result, array('name' => 'VisitSummary'));
+
+ $installer = new PiwikInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'visit-summary'));
+ $this->assertEquals($result, array('name' => 'VisitSummary'));
+
+ $installer = new PiwikInstaller($this->package, $this->composer);
+ $result = $installer->inflectPackageVars(array('name' => 'visit_summary'));
+ $this->assertEquals($result, array('name' => 'VisitSummary'));
+ }
+
+}
diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/TestCase.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/TestCase.php
new file mode 100644
index 000000000..6418a03b8
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/TestCase.php
@@ -0,0 +1,64 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Installers\Test;
+
+use Composer\Package\Version\VersionParser;
+use Composer\Package\Package;
+use Composer\Package\AliasPackage;
+use Composer\Package\LinkConstraint\VersionConstraint;
+use Composer\Util\Filesystem;
+
+abstract class TestCase extends \PHPUnit_Framework_TestCase
+{
+ private static $parser;
+
+ protected static function getVersionParser()
+ {
+ if (!self::$parser) {
+ self::$parser = new VersionParser();
+ }
+
+ return self::$parser;
+ }
+
+ protected function getVersionConstraint($operator, $version)
+ {
+ return new VersionConstraint(
+ $operator,
+ self::getVersionParser()->normalize($version)
+ );
+ }
+
+ protected function getPackage($name, $version)
+ {
+ $normVersion = self::getVersionParser()->normalize($version);
+
+ return new Package($name, $normVersion, $version);
+ }
+
+ protected function getAliasPackage($package, $version)
+ {
+ $normVersion = self::getVersionParser()->normalize($version);
+
+ return new AliasPackage($package, $normVersion, $version);
+ }
+
+ protected function ensureDirectoryExistsAndClear($directory)
+ {
+ $fs = new Filesystem();
+ if (is_dir($directory)) {
+ $fs->removeDirectory($directory);
+ }
+ mkdir($directory, 0777, true);
+ }
+}
diff --git a/web/api/app/vendor/composer/installers/tests/bootstrap.php b/web/api/app/vendor/composer/installers/tests/bootstrap.php
new file mode 100644
index 000000000..30c8fdc67
--- /dev/null
+++ b/web/api/app/vendor/composer/installers/tests/bootstrap.php
@@ -0,0 +1,4 @@
+add('Composer\Installers\Test', __DIR__);
diff --git a/web/api/app/webroot/.htaccess b/web/api/app/webroot/.htaccess
index 1f19e4c06..f08afa8b2 100644
--- a/web/api/app/webroot/.htaccess
+++ b/web/api/app/webroot/.htaccess
@@ -3,4 +3,5 @@
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
+ RewriteBase /zm/api
diff --git a/web/includes/actions.php b/web/includes/actions.php
index 2f23e0d59..2e813a957 100644
--- a/web/includes/actions.php
+++ b/web/includes/actions.php
@@ -642,7 +642,35 @@ if ( !empty($action) )
// System edit actions
if ( canEdit( 'System' ) )
{
- if ( $action == "version" && isset($_REQUEST['option']) )
+ 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 if ( $action == "version" && isset($_REQUEST['option']) )
{
$option = $_REQUEST['option'];
switch( $option )
diff --git a/web/lang/de_de.php b/web/lang/de_de.php
index 9777fe2dd..1d219382a 100644
--- a/web/lang/de_de.php
+++ b/web/lang/de_de.php
@@ -20,6 +20,7 @@
// ZoneMinder german Translation by Robert Schumann (rs at core82 dot de)
// ZoneMinder german Translation by Sebastian Kaminski (github @seeebek)
+// german Translation update by seebaer1976
// Notes for Translators
// 0. Get some credit, put your name in the line above (optional)
@@ -109,7 +110,7 @@ $SLANG = array(
'AttrCause' => 'Grund',
'AttrDate' => 'Datum',
'AttrDateTime' => 'Datum/Zeit',
- 'AttrDiskBlocks' => 'Disk-Bloecke',
+ 'AttrDiskBlocks' => 'Disk-Blöcke',
'AttrDiskPercent' => 'Disk-Prozent',
'AttrDuration' => 'Dauer',
'AttrFrames' => 'Bilder',
@@ -125,14 +126,14 @@ $SLANG = array(
'AttrWeekday' => 'Wochentag',
'Auto' => 'Auto',
'AutoStopTimeout' => 'Auto-Stopp-Zeitüberschreitung',
- 'Available' => 'Vorhanden', // Added - 2009-03-31
+ 'Available' => 'Verfügbar', // Added - 2009-03-31
'AvgBrScore' => 'Mittlere Punktzahl',
'Background' => 'Hintergrund',
'BackgroundFilter' => 'Filter im Hintergrund laufen lassen',
'BadAlarmFrameCount' => 'Die Bildanzahl muss ganzzahlig 1 oder größer sein',
'BadAlarmMaxFPS' => 'Alarm-Maximum-FPS muss eine positive Ganzzahl oder eine Gleitkommazahl sein',
'BadChannel' => 'Der Kanal muss ganzzahlig 0 oder größer sein',
- 'BadColours' => 'Die Zielfarbe muss eine gültige Farbe sein.', // Added - 2011-06-15
+ 'BadColours' => 'Zielfarbe muss auf einen gültigen Wert gesetzt werden', // Added - 2011-06-15
'BadDevice' => 'Das Gerät muss eine gültige Systemresource sein',
'BadFPSReportInterval' => 'Der FPS-Intervall-Puffer-Zähler muss ganzzahlig 0 oder größer sein',
'BadFormat' => 'Das Format muss ganzzahlig 0 oder größer sein',
@@ -143,9 +144,9 @@ $SLANG = array(
'BadLabelX' => 'Die x-Koordinate der Bezeichnung muss ganzzahlig 0 oder größer sein',
'BadLabelY' => 'Die y-Koordinate der Bezeichnung muss ganzzahlig 0 oder größer sein',
'BadMaxFPS' => 'Maximum-FPS muss eine positive Ganzzahl oder eine Gleitkommazahl sein',
- 'BadMotionFrameSkip' => 'Motion Frame skip count must be an integer of zero or more',
+ 'BadMotionFrameSkip' => 'Bewegungsrahmen Skip-Zählung muß eine ganze Zahl von null oder mehr betragen,',
'BadNameChars' => 'Namen dürfen nur aus Buchstaben, Zahlen und Trenn- oder Unterstrichen bestehen',
- 'BadPalette' => 'Die Palette muss auf einen gültigen Wert eingestellt sein', // Added - 2009-03-31
+ 'BadPalette' => 'Palette muss auf einen gültigen Wert gesetzt sein', // Added - 2009-03-31
'BadPath' => 'Der Pfad muss auf einen gültigen Wert eingestellt sein',
'BadPort' => 'Der Port muss auf eine gültige Zahl eingestellt sein',
'BadPostEventCount' => 'Der Zähler für die Ereignisfolgebilder muss ganzzahlig 0 oder größer sein',
@@ -153,7 +154,7 @@ $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',
- 'BadStreamReplayBuffer'=> 'Der Wiedergabestrompuffer tream replay buffer must be an integer of zero or more',
+ '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',
'BadWidth' => 'Die Breite muss auf einen gültigen Wert eingestellt sein',
@@ -168,7 +169,7 @@ $SLANG = array(
'CSSDescription' => 'Ändere das standard CSS für diesen Computer.', // Added - 2015-04-18
'CanAutoFocus' => 'Kann Autofokus',
'CanAutoGain' => 'Kann Auto-Verstärkung',
- 'CanAutoIris' => 'Kann Auto-Iris',
+ 'CanAutoIris' => 'Kann Auto-Blende',
'CanAutoWhite' => 'Kann Auto-Weiß-Abgleich',
'CanAutoZoom' => 'Kann Auto-Zoom',
'CanFocus' => 'Kann Fokus',
@@ -178,12 +179,12 @@ $SLANG = array(
'CanGain' => 'Kann Verstärkung',
'CanGainAbs' => 'Kann absolute Verstärkung',
'CanGainCon' => 'Kann kontinuierliche Verstärkung',
- 'CanGainRel' => 'Kann relative Verstäkung',
- 'CanIris' => 'Kann Iris',
- 'CanIrisAbs' => 'Kann absolute Iris',
- 'CanIrisCon' => 'Kann kontinuierliche Iris',
- 'CanIrisRel' => 'Kann relative Iris',
- 'CanMove' => 'Kann Bewegung',
+ 'CanGainRel' => 'Kann relative Verstärkung',
+ 'CanIris' => 'Kann Blende',
+ 'CanIrisAbs' => 'Kann absolute Blende',
+ 'CanIrisCon' => 'Kann kontinuierliche Blende',
+ 'CanIrisRel' => 'Kann relative Blende',
+ 'CanMove' => 'Kann sich Bewegung',
'CanMoveAbs' => 'Kann absolute Bewegung',
'CanMoveCon' => 'Kann kontinuierliche Bewegung',
'CanMoveDiag' => 'Kann diagonale Bewegung',
@@ -197,7 +198,7 @@ $SLANG = array(
'CanWake' => 'Kann Wake',
'CanWhite' => 'Kann Weiß-Abgleich',
'CanWhiteAbs' => 'Kann absoluten Weiß-Abgleich',
- 'CanWhiteBal' => 'Kann Weiß-Abgleich',
+ 'CanWhiteBal' => 'Kann Weiß-Abgleich-Balance',
'CanWhiteCon' => 'Kann kontinuierlichen Weiß-Abgleich',
'CanWhiteRel' => 'Kann relativen Weiß-Abgleich',
'CanZoom' => 'Kann Zoom',
@@ -206,17 +207,17 @@ $SLANG = array(
'CanZoomRel' => 'Kann relativen Zoom',
'Cancel' => 'Abbruch',
'CancelForcedAlarm' => 'Abbruch des unbedingten Alarms',
- 'CaptureHeight' => 'Erfasse Höhe',
- 'CaptureMethod' => 'Aufnahmemethode', // Added - 2009-02-08
- 'CapturePalette' => 'Erfasse Farbpalette',
+ 'CaptureHeight' => 'Erfassungshöhe',
+ 'CaptureMethod' => 'Erfassungsmethode', // Added - 2009-02-08
+ 'CapturePalette' => 'Erfassungsfarbpalette',
'CaptureResolution' => 'Aufnahmeauflösung', // Added - 2015-04-18
- 'CaptureWidth' => 'Erfasse Breite',
+ 'CaptureWidth' => 'Erfassungsbreite',
'Cause' => 'Grund',
'CheckMethod' => 'Alarm-Prüfmethode',
- 'ChooseDetectedCamera' => 'Wähle erkannte Kamera', // Added - 2009-03-31
+ 'ChooseDetectedCamera' => 'Erkannte Kamera wählen', // Added - 2009-03-31
'ChooseFilter' => 'Filterauswahl',
- 'ChooseLogFormat' => 'Wähle ein Log-Format', // Added - 2011-06-17
- 'ChooseLogSelection' => 'Wähle eine Log-Auswahl', // Added - 2011-06-17
+ 'ChooseLogFormat' => 'Log-Format wählen', // Added - 2011-06-17
+ 'ChooseLogSelection' => 'Log-Auswahl', // Added - 2011-06-17
'ChoosePreset' => 'Voreinstellung auswählen',
'Clear' => 'Leeren', // Added - 2011-06-16
'Close' => 'Schließen',
@@ -253,8 +254,8 @@ $SLANG = array(
'Deinterlacing' => 'Deinterlacing', // Added - 2015-04-18
'Delay' => 'Verzögerung', // Added - 2015-04-18
'Delete' => 'Löschen',
- 'DeleteAndNext' => 'Löschen & Nächstes',
- 'DeleteAndPrev' => 'Löschen & Vorheriges',
+ 'DeleteAndNext' => 'Löschen & Nächstes',
+ 'DeleteAndPrev' => 'Löschen & Vorheriges',
'DeleteSavedFilter' => 'Lösche gespeichertes Filter',
'Description' => 'Beschreibung',
'DetectedCameras' => 'Erkannte Kameras', // Added - 2009-03-31
@@ -281,7 +282,7 @@ $SLANG = array(
'DonateRemindWeek' => 'Noch nicht, erinnere mich in einer Woche noch mal.',
'DonateYes' => 'Ja, ich möchte jetzt spenden.',
'Download' => 'Download',
- 'DuplicateMonitorName' => 'Duplizierter Monitorname', // Added - 2009-03-31
+ 'DuplicateMonitorName' => 'Monitornamen Duplizieren', // Added - 2009-03-31
'Duration' => 'Dauer',
'Edit' => 'Bearbeiten',
'Email' => 'E-Mail',
@@ -308,14 +309,14 @@ $SLANG = array(
'ExportFormatZip' => 'ZIP (Komprimiert)',
'ExportFrames' => 'Exportiere Bilddetails',
'ExportImageFiles' => 'Exportiere Bilddateien',
- 'ExportLog' => 'Export Log', // Added - 2011-06-17
+ 'ExportLog' => 'Log Exportieren', // Added - 2011-06-17
'ExportMiscFiles' => 'Exportiere andere Dateien (falls vorhanden)',
- 'ExportOptions' => 'Exportierungsoptionen',
- 'ExportSucceeded' => 'Export erfolgreich', // Added - 2009-02-08
+ 'ExportOptions' => 'Exportoptionen',
+ 'ExportSucceeded' => 'Export Erfolgreich', // Added - 2009-02-08
'ExportVideoFiles' => 'Exportiere Videodateien (falls vorhanden)',
'Exporting' => 'Exportiere',
- 'FPS' => 'fps',
- 'FPSReportInterval' => 'fps-Meldeintervall',
+ 'FPS' => 'FPS',
+ 'FPSReportInterval' => 'FPS-Meldeintervall',
'FTP' => 'FTP',
'Far' => 'Weit',
'FastForward' => 'Schnell vorwärts',
@@ -344,7 +345,7 @@ $SLANG = array(
'FnNone' => 'Keine', // Added 2013.08.16.
'FnRecord' => 'Record', // Added 2013.08.16.
'Focus' => 'Fokus',
- 'ForceAlarm' => 'Erzwinge Alarm',
+ 'ForceAlarm' => 'Alarm erzwingen',
'Format' => 'Format',
'Frame' => 'Bild',
'FrameId' => 'Bild-ID',
@@ -362,9 +363,9 @@ $SLANG = array(
'Group' => 'Gruppe',
'Groups' => 'Gruppen',
'HasFocusSpeed' => 'Hat Fokus-Geschwindigkeit',
- 'HasGainSpeed' => 'Hat Verstäkungs-Geschwindigkeit',
+ 'HasGainSpeed' => 'Hat Verstärkungs-Geschwindigkeit',
'HasHomePreset' => 'Hat Standardvoreinstellungen',
- 'HasIrisSpeed' => 'Hat Irisgeschwindigkeit',
+ 'HasIrisSpeed' => 'Hat Blendengeschwindigkeit',
'HasPanSpeed' => 'Hat Pan-Geschwindigkeit',
'HasPresets' => 'Hat Voreinstellungen',
'HasTiltSpeed' => 'Hat Neigungsgeschwindigkeit',
@@ -387,7 +388,7 @@ $SLANG = array(
'Include' => 'Einschluss',
'IncludeNatDet' => 'Include Native Detection', // Added - 2015-05-06
'Inverted' => 'Invertiert',
- 'Iris' => 'Iris',
+ 'Iris' => 'Blende',
'IsNotAValidOption' => 'is not a valid option', // Added - 2015-05-28
'KeyString' => 'Schlüsselwort',
'Label' => 'Bezeichnung',
@@ -425,9 +426,9 @@ $SLANG = array(
'MaxGainRange' => 'Maximaler Verstärkungsbereich',
'MaxGainSpeed' => 'Maximale Verstärkungsgeschwindigkeit',
'MaxGainStep' => 'Maximale Verstärkungsstufe',
- 'MaxIrisRange' => 'Maximaler Irisbereich',
- 'MaxIrisSpeed' => 'Maximale Irisgeschwindigkeit',
- 'MaxIrisStep' => 'Maximale Irisstufe',
+ 'MaxIrisRange' => 'Maximaler Blendenbereich',
+ 'MaxIrisSpeed' => 'Maximale Blendengeschwindigkeit',
+ 'MaxIrisStep' => 'Maximale Blendenstufe',
'MaxPanRange' => 'Maximaler Pan-Bereich',
'MaxPanSpeed' => 'Maximale Pan-Geschw.',
'MaxPanStep' => 'Maximale Pan-Stufe',
@@ -460,9 +461,9 @@ $SLANG = array(
'MinGainRange' => 'Min. Verstärkungsbereich',
'MinGainSpeed' => 'Min. Verstärkungsgeschwindigkeit',
'MinGainStep' => 'Min. Verstärkungsstufe',
- 'MinIrisRange' => 'Min. Irisbereich',
- 'MinIrisSpeed' => 'Min. Irisgeschwindigkeit',
- 'MinIrisStep' => 'Min. Irisstufe',
+ 'MinIrisRange' => 'Min. Blendenbereich',
+ 'MinIrisSpeed' => 'Min. Blendengeschwindigkeit',
+ 'MinIrisStep' => 'Min. Blendenstufe',
'MinPanRange' => 'Min. Pan-Bereich',
'MinPanSpeed' => 'Min. Pan-Geschwindigkeit',
'MinPanStep' => 'Min. Pan-Stufe',
@@ -496,7 +497,7 @@ $SLANG = array(
'Mtg3widgrx' => '3 Spalten, skaliert, vergr. bei Alarm', // Added 2013.08.15.
'Mtg4widgrd' => '4 Spalten', // Added 2013.08.15.
'MtgDefault' => 'Standard', // Added 2013.08.15.
- 'MustBeGe' => 'muss groesser oder gleich sein wie',
+ 'MustBeGe' => 'muss größer oder gleich sein wie',
'MustBeLe' => 'muss kleiner oder gleich sein wie',
'MustConfirmPassword' => 'Sie müssen das Passwort bestätigen.',
'MustSupplyPassword' => 'Sie müssen ein Passwort vergeben.',
@@ -550,8 +551,8 @@ $SLANG = array(
'OverwriteExisting' => 'überschreibe bestehende',
'Paged' => 'Seitennummeriert',
'Pan' => 'Pan',
- 'PanLeft' => 'Pan-Left',
- 'PanRight' => 'Pan-Right',
+ 'PanLeft' => 'Pan-Links',
+ 'PanRight' => 'Pan-Rechts',
'PanTilt' => 'Pan/Neigung',
'Parameter' => 'Parameter',
'Password' => 'Passwort',
@@ -575,7 +576,7 @@ $SLANG = array(
'Preset' => 'Voreinstellung',
'Presets' => 'Voreinstellungen',
'Prev' => 'Vorheriges',
- 'Probe' => 'Sonde', // Added - 2009-03-31
+ 'Probe' => 'Suchen', // Added - 2009-03-31
'ProfileProbe' => 'Streamsonde', // Added - 2015-04-18
'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
@@ -612,7 +613,7 @@ $SLANG = array(
'Rewind' => 'Zurückspulen',
'RotateLeft' => 'Drehung links',
'RotateRight' => 'Drehung rechts',
- 'RunLocalUpdate' => 'Bitte zmupdate.pl starten um upzudaten.', // Added - 2011-05-25
+ 'RunLocalUpdate' => 'Für Update "zmupdate.pl" ausführen', // Added - 2011-05-25
'RunMode' => 'Betriebsmodus',
'RunState' => 'Laufender Status',
'Running' => 'In Betrieb',
@@ -624,8 +625,8 @@ $SLANG = array(
'Secs' => 'Sekunden',
'Sectionlength' => 'Sektionslänge',
'Select' => 'Auswahl',
- 'SelectFormat' => 'Format Auswahl', // Added - 2011-06-17
- 'SelectLog' => 'Log Auswahl', // Added - 2011-06-17
+ 'SelectFormat' => 'Format auswählen', // Added - 2011-06-17
+ 'SelectLog' => 'Log auswählen', // Added - 2011-06-17
'SelectMonitors' => 'Wähle Monitore',
'SelfIntersecting' => 'Die Polygonränder dürfen sich nicht überschneiden.',
'Set' => 'Setze',
@@ -652,16 +653,16 @@ $SLANG = array(
'SpeedTurbo' => 'Turbo-Geschwindigkeit',
'Start' => 'Start',
'State' => 'Status',
- 'Stats' => 'Status',
+ 'Stats' => 'Statistik',
'Status' => 'Status',
'Step' => 'Stufe',
'StepBack' => 'Einen Schritt rückwärts',
'StepForward' => 'Einen Schritt vorwärts',
- 'StepLarge' => 'Große Stufe',
- 'StepMedium' => 'Mittlere Stufe',
- 'StepNone' => 'Keine Stufe',
- 'StepSmall' => 'Kleine Stufe',
- 'Stills' => 'Bilder',
+ 'StepLarge' => 'Großer Schritt',
+ 'StepMedium' => 'Mittlere Schhritt',
+ 'StepNone' => 'Keine Schritt',
+ 'StepSmall' => 'Kleiner Schritt',
+ 'Stills' => 'Standbilder',
'Stop' => 'Stop',
'Stopped' => 'Gestoppt',
'Stream' => 'Stream',
@@ -671,16 +672,16 @@ $SLANG = array(
'SystemLog' => 'System Log', // Added - 2011-06-16
'TargetColorspace' => 'Zielfarbbereich', // Added - 2015-04-18
'Tele' => 'Tele',
- 'Thumbnail' => 'Miniatur',
+ 'Thumbnail' => 'Miniaturbild',
'Tilt' => 'Neigung',
'Time' => 'Zeit',
'TimeDelta' => 'Zeitdifferenz',
'TimeStamp' => 'Zeitstempel',
'Timeline' => 'Zeitlinie',
- 'TimelineTip1' => 'Bewege die Maus über dem Graphen um eine Vorschau und Ereignisdetails zu sehen.', // Added 2013.08.15.
- 'TimelineTip2' => 'Clicke auf den farbigen Abschnitt des Graphen oder auf das Bild um den Ereignis zu betrachten', // Added 2013.08.15.
- 'TimelineTip3' => 'Clicke auf den Hintergrund, um in den Zeitabschnitt um das Ereignis hereinzuzoomen.', // Added 2013.08.15.
- 'TimelineTip4' => 'Benutze die unteren Bedienelemente um herauszuzoomen oder sich in der Zeitleiste zu bewegen.', // Added 2013.08.15.
+ 'TimelineTip1' => 'Fahren Sie mit der Maus über die Grafik, um eine Momentaufnahme der Bild- und Ereignisdetails zusehen.', // Added 2013.08.15.
+ 'TimelineTip2' => 'Klicken Sie auf den farbig markierten Bereichen der Grafik oder das Bild, um das Ereignis zu sehen.', // Added 2013.08.15.
+ 'TimelineTip3' => 'Klicken Sie auf den Hintergrund, um in einen kleineren Zeitraum zu vergrößern.', // Added 2013.08.15.
+ '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',
'TimestampLabelX' => 'Zeitstempel-X',
@@ -777,7 +778,7 @@ $CLANG = array(
'MonitorCount' => '%1$s %2$s', // For example '4 Monitors' (from Vlang below)
'MonitorFunction' => 'Monitor %1$s Funktion',
'RunningRecentVer' => 'Sie benutzen die aktuellste Version von Zoneminder, v%s.',
- 'VersionMismatch' => 'Version mismatch, system is version %1$s, database is %2$s.', // Added - 2011-05-25
+ 'VersionMismatch' => 'Versionskonflikt, System-Version ist %1$s , Datenbank-Version ist %2$s.', // Added - 2011-05-25
);
// The next section allows you to describe a series of word ending and counts used to
diff --git a/web/skins/classic/views/console.php b/web/skins/classic/views/console.php
index bf33802d9..5930ebe95 100644
--- a/web/skins/classic/views/console.php
+++ b/web/skins/classic/views/console.php
@@ -70,6 +70,8 @@ $eventCounts = array(
$running = daemonCheck();
$status = $running?translate('Running'):translate('Stopped');
+$run_state_array = dbFetchOne('select Name from States where IsActive = 1');
+$run_state = implode($run_state_array);
$group = NULL;
if ( ! empty($_COOKIE['zmGroup']) ) {
@@ -188,7 +190,7 @@ xhtmlHeaders( __FILE__, translate('Console') );