Merge branch 'storageareas' into zma_to_thread

This commit is contained in:
Isaac Connor 2017-10-06 17:10:28 -04:00
commit 224ca30c86
80 changed files with 1952 additions and 1022 deletions

View File

@ -172,7 +172,7 @@ set(ZM_PATH_ARP "" CACHE PATH
"Full path to compatible arp binary. Leave empty for automatic detection.") "Full path to compatible arp binary. Leave empty for automatic detection.")
set(ZM_CONFIG_DIR "/${CMAKE_INSTALL_SYSCONFDIR}" CACHE PATH set(ZM_CONFIG_DIR "/${CMAKE_INSTALL_SYSCONFDIR}" CACHE PATH
"Location of ZoneMinder configuration, default system config directory") "Location of ZoneMinder configuration, default system config directory")
set(ZM_CONFIG_SUBDIR "${ZM_CONFIG_DIR}/zm/conf.d" CACHE PATH set(ZM_CONFIG_SUBDIR "${ZM_CONFIG_DIR}/conf.d" CACHE PATH
"Location of ZoneMinder configuration subfolder, default: ZM_CONFIG_DIR/conf.d") "Location of ZoneMinder configuration subfolder, default: ZM_CONFIG_DIR/conf.d")
set(ZM_EXTRA_LIBS "" CACHE STRING set(ZM_EXTRA_LIBS "" CACHE STRING
"A list of optional libraries, separated by semicolons, e.g. ssl;theora") "A list of optional libraries, separated by semicolons, e.g. ssl;theora")

View File

@ -58,6 +58,7 @@ RUN apt-get update \
php-mysql \ php-mysql \
vlc-data \ vlc-data \
yasm \ yasm \
zip \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# Copy local code into our container # Copy local code into our container

View File

@ -264,7 +264,8 @@ DROP TABLE IF EXISTS `Groups`;
CREATE TABLE `Groups` ( CREATE TABLE `Groups` (
`Id` int(10) unsigned NOT NULL auto_increment, `Id` int(10) unsigned NOT NULL auto_increment,
`Name` varchar(64) NOT NULL default '', `Name` varchar(64) NOT NULL default '',
`MonitorIds` tinytext NOT NULL, `ParentId` int(10) unsigned,
`MonitorIds` text NOT NULL,
PRIMARY KEY (`Id`) PRIMARY KEY (`Id`)
) ENGINE=@ZM_MYSQL_ENGINE@; ) ENGINE=@ZM_MYSQL_ENGINE@;
@ -286,6 +287,31 @@ CREATE TABLE `Logs` (
KEY `TimeKey` (`TimeKey`) KEY `TimeKey` (`TimeKey`)
) ENGINE=@ZM_MYSQL_ENGINE@; ) ENGINE=@ZM_MYSQL_ENGINE@;
--
-- Table structure for table `Manufacturers`
--
DROP TABLE IF EXISTS `Manufacturers`;
CREATE TABLE `Manufacturers` (
`Id` int(10) unsigned NOT NULL auto_increment,
`Name` varchar(64) NOT NULL,
PRIMARY KEY (`Id`),
UNIQUE KEY (`Name`)
) ENGINE=@ZM_MYSQL_ENGINE@;
--
-- Table structure for table `Models`
--
DROP TABLE IF EXISTS `Models`;
CREATE TABLE `Models` (
`Id` int(10) unsigned NOT NULL auto_increment,
`Name` varchar(64) NOT NULL,
`ManufacturerId` int(10),
PRIMARY KEY (`Id`),
UNIQUE KEY (`ManufacturerId`,`Name`)
) ENGINE=@ZM_MYSQL_ENGINE@;
-- --
-- Table structure for table `MonitorPresets` -- Table structure for table `MonitorPresets`
-- --
@ -294,7 +320,7 @@ DROP TABLE IF EXISTS `MonitorPresets`;
CREATE TABLE `MonitorPresets` ( CREATE TABLE `MonitorPresets` (
`Id` int(10) unsigned NOT NULL auto_increment, `Id` int(10) unsigned NOT NULL auto_increment,
`Name` varchar(64) NOT NULL default '', `Name` varchar(64) NOT NULL default '',
`Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL') NOT NULL default 'Local', `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','NVSocket') NOT NULL default 'Local',
`Device` tinytext, `Device` tinytext,
`Channel` tinytext, `Channel` tinytext,
`Format` int(10) unsigned default NULL, `Format` int(10) unsigned default NULL,

View File

@ -52,7 +52,7 @@ SET @s = (SELECT IF(
AND column_name = 'DefaultVideo' AND column_name = 'DefaultVideo'
) > 0, ) > 0,
"SELECT 'Column DefaultVideo exists in Events'", "SELECT 'Column DefaultVideo exists in Events'",
"ALTER TABLE `Events` ADD `DefaultVideo` VARCHAR( 64 ) NOT NULL AFTER `AlarmFrames`" "ALTER TABLE `Events` ADD `DefaultVideo` VARCHAR( 64 ) NOT NULL default '' AFTER `AlarmFrames`"
)); ));
PREPARE stmt FROM @s; PREPARE stmt FROM @s;

42
db/zm_update-1.31.4.sql Normal file
View File

@ -0,0 +1,42 @@
--
-- This adds Manufacturers and Models
--
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.TABLES
WHERE table_name = 'Manufacturers'
AND table_schema = DATABASE()
) > 0,
"SELECT 'Manufacturers table exists'",
"
CREATE TABLE `Manufacturers` (
`Id` int(10) unsigned NOT NULL auto_increment,
`Name` varchar(64) NOT NULL,
PRIMARY KEY (`Id`),
UNIQUE KEY (`Name`)
)"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.TABLES
WHERE table_name = 'Models'
AND table_schema = DATABASE()
) > 0,
"SELECT 'Models table exists'",
"CREATE TABLE `Models` (
`Id` int(10) unsigned NOT NULL auto_increment,
`Name` varchar(64) NOT NULL,
`ManufacturerId` int(10),
PRIMARY KEY (`Id`),
UNIQUE KEY (`ManufacturerId`,`Name`)
)"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

17
db/zm_update-1.31.5.sql Normal file
View File

@ -0,0 +1,17 @@
--
-- Add StorageId column to Monitors
--
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Groups'
AND table_schema = DATABASE()
AND column_name = 'ParentId'
) > 0,
"SELECT 'Column GroupId exists in Groups'",
"ALTER TABLE Groups ADD `ParentId` int(10) unsigned AFTER `Name`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

1
db/zm_update-1.31.6.sql Normal file
View File

@ -0,0 +1 @@
ALTER TABLE Monitors MODIFY `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','NVSocket') NOT NULL default 'Local';

1
db/zm_update-1.31.7.sql Normal file
View File

@ -0,0 +1 @@
ALTER TABLE Groups MODIFY `MonitorIds` text NOT NULL;

View File

@ -166,7 +166,7 @@ too much degradation of performance.
find %{buildroot} \( -name .htaccess -or -name .editorconfig -or -name .packlist -or -name .git -or -name .gitignore -or -name .gitattributes -or -name .travis.yml \) -type f -delete > /dev/null 2>&1 || : find %{buildroot} \( -name .htaccess -or -name .editorconfig -or -name .packlist -or -name .git -or -name .gitignore -or -name .gitattributes -or -name .travis.yml \) -type f -delete > /dev/null 2>&1 || :
# Recursively change shebang in all relevant scripts and set execute permission # Recursively change shebang in all relevant scripts and set execute permission
find %{buildroot}%{_datadir}/zoneminder/www/api \( -name cake -or -name cake.php \) -type f -exec sed -i 's\^#!/usr/bin/env bash$\#!/usr/bin/bash\' {} \; -exec %{__chmod} 755 {} \; find %{buildroot}%{_datadir}/zoneminder/www/api \( -name cake -or -name cake.php \) -type f -exec sed -i 's\^#!/usr/bin/env bash$\#!%{_buildshell}\' {} \; -exec %{__chmod} 755 {} \;
# Use the system cacert file rather then the one bundled with CakePHP # Use the system cacert file rather then the one bundled with CakePHP
%{__rm} -f %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem %{__rm} -f %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem

View File

@ -0,0 +1,7 @@
zoneminder (1.31.4-vivid1) vivid; urgency=medium
* Release 1.31.4
-- Isaac Connor <iconnor@tesla.com> Thu, 21 Sep 2017 09:55:31 -0700

View File

@ -8,29 +8,4 @@ if [ -h "${ol}" ]; then
[ "$(readlink ${ol})" = "/etc/zm/apache.conf" ] && rm -f "${ol}" [ "$(readlink ${ol})" = "/etc/zm/apache.conf" ] && rm -f "${ol}"
fi fi
abort=false
if [ -h /usr/share/zoneminder/www/events ]; then
l=$(readlink /usr/share/zoneminder/www/events)
if [ "$l" != "/var/cache/zoneminder/events" -a "$l" != "/var/cache/zoneminder/events/" ]; then
abort=true
fi
fi
if [ -h /usr/share/zoneminder/www/images ]; then
l=$(readlink /usr/share/zoneminder/www/images )
if [ "$l" != "/var/cache/zoneminder/images" -a "$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# #DEBHELPER#

View File

@ -899,6 +899,21 @@ our @options = (
type => $types{integer}, type => $types{integer},
category => 'network', category => 'network',
}, },
{
name => 'ZM_MIN_STREAMING_PORT',
default => '',
description => 'Alternate port range to contact for streaming video.',
help => q`
Due to browsers only wanting to open 6 connections, if you have more
than 6 monitors, you can have trouble viewing more than 6. This setting
specified the beginning of a port range that will be used to contact ZM
on. Each monitor will use this value plus the Monitor Id to stream
content. So a value of 2000 here will cause a stream for Monitor 1 to
hit port 2001. Please ensure that you configure apache appropriately
to respond on these ports.`,
type => $types{integer},
category => 'network',
},
{ {
name => 'ZM_MIN_RTP_PORT', name => 'ZM_MIN_RTP_PORT',
default => '40200', default => '40200',

View File

@ -348,7 +348,12 @@ Debug("Checking for files for event $_[0]{Id} at $path using glob $path/* found
sub age { sub age {
if ( ! $_[0]{age} ) { if ( ! $_[0]{age} ) {
if ( -e $_[0]->Path() ) {
# $^T is the time the program began running. -M is program start time - file modification time in days
$_[0]{age} = (time() - ($^T - ((-M $_[0]->Path() ) * 24*60*60))); $_[0]{age} = (time() - ($^T - ((-M $_[0]->Path() ) * 24*60*60)));
} else {
Warning($_[0]->Path() . ' does not appear to exist.');
}
} }
return $_[0]{age}; return $_[0]{age};
} }

View File

@ -4,7 +4,7 @@
configure_file(zm_config.h.in "${CMAKE_CURRENT_BINARY_DIR}/zm_config.h" @ONLY) configure_file(zm_config.h.in "${CMAKE_CURRENT_BINARY_DIR}/zm_config.h" @ONLY)
# Group together all the source files that are used by all the binaries (zmc, zma, zmu, zms etc) # Group together all the source files that are used by all the binaries (zmc, zma, zmu, zms etc)
set(ZM_BIN_SRC_FILES zm_analysis_thread.cpp zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_camera.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.cpp zm_ffmpeg.cpp zm_ffmpeg_input.cpp zm_mpeg.cpp zm_packet.cpp zm_packetqueue.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_swscale.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp zm_storage.cpp) set(ZM_BIN_SRC_FILES zm_analysis_thread.cpp zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_camera.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.cpp zm_ffmpeg.cpp zm_ffmpeg_input.cpp zm_mpeg.cpp zm_packet.cpp zm_packetqueue.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_nvsocket.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_swscale.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp zm_storage.cpp)
# A fix for cmake recompiling the source files for every target. # A fix for cmake recompiling the source files for every target.
add_library(zm STATIC ${ZM_BIN_SRC_FILES}) add_library(zm STATIC ${ZM_BIN_SRC_FILES})

View File

@ -124,7 +124,7 @@ MYSQL_RES *zmDbRow::fetch( const char *query ) {
result_set = NULL; result_set = NULL;
Error("Error getting row from query %s. Error is %s", query, mysql_error( &dbconn ) ); Error("Error getting row from query %s. Error is %s", query, mysql_error( &dbconn ) );
} else { } else {
Debug(3, "Succes"); Debug(5, "Success");
} }
return result_set; return result_set;
} }

View File

@ -145,7 +145,7 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string
// Create event id symlink // Create event id symlink
snprintf( id_file, sizeof(id_file), "%s/.%d", date_path, id ); snprintf( id_file, sizeof(id_file), "%s/.%d", date_path, id );
if ( symlink( time_path, id_file ) < 0 ) if ( symlink( time_path, id_file ) < 0 )
Fatal( "Can't symlink %s -> %s: %s", id_file, path, strerror(errno)); Error( "Can't symlink %s -> %s: %s", id_file, path, strerror(errno));
} else { } else {
snprintf( path, sizeof(path), "%s/%d/%d", storage->Path(), monitor->Id(), id ); snprintf( path, sizeof(path), "%s/%d/%d", storage->Path(), monitor->Id(), id );
@ -156,14 +156,14 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string
Error( "Can't mkdir %s: %s", path, strerror(errno)); Error( "Can't mkdir %s: %s", path, strerror(errno));
} }
} }
} // deep storage or not
// Create empty id tag file // Create empty id tag file
snprintf( id_file, sizeof(id_file), "%s/.%d", path, id ); snprintf( id_file, sizeof(id_file), "%s/.%d", path, id );
if ( FILE *id_fp = fopen( id_file, "w" ) ) if ( FILE *id_fp = fopen( id_file, "w" ) )
fclose( id_fp ); fclose( id_fp );
else else
Fatal( "Can't fopen %s: %s", id_file, strerror(errno)); Error( "Can't fopen %s: %s", id_file, strerror(errno));
} // deep storage or not
last_db_frame = 0; last_db_frame = 0;
@ -262,22 +262,22 @@ void Event::createNotes( std::string &notes ) {
int Event::sd = -1; int Event::sd = -1;
bool Event::WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame ) { bool Event::WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame ) {
Image* ImgToWrite;
Image* ts_image = NULL;
if ( !config.timestamp_on_capture ) // stash the image we plan to use in another pointer regardless if timestamped.
{
ts_image = new Image(*image);
monitor->TimestampImage( ts_image, &timestamp );
ImgToWrite=ts_image;
} else
ImgToWrite=image;
int thisquality = ( alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality) ) ? config.jpeg_alarm_file_quality : 0 ; // quality to use, zero is default int thisquality = ( alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality) ) ? config.jpeg_alarm_file_quality : 0 ; // quality to use, zero is default
ImgToWrite->WriteJpeg( event_file, thisquality, (monitor->Exif() ? timestamp : (timeval){0,0}) ); // exif is only timestamp at present this switches on or off for write bool rc;
Debug(3, "Writing image to %s", event_file );
if(ts_image) delete(ts_image); // clean up if used. if ( !config.timestamp_on_capture ) {
return( true ); // stash the image we plan to use in another pointer regardless if timestamped.
Image *ts_image = new Image(*image);
monitor->TimestampImage( ts_image, &timestamp );
rc = ts_image->WriteJpeg( event_file, thisquality, (monitor->Exif() ? timestamp : (timeval){0,0}) ); // exif is only timestamp at present this switches on or off for write
delete(ts_image);
} else {
rc = image->WriteJpeg( event_file, thisquality, (monitor->Exif() ? timestamp : (timeval){0,0}) ); // exif is only timestamp at present this switches on or off for write
}
return rc;
} }
bool Event::WriteFrameVideo( const Image *image, const struct timeval timestamp, VideoWriter* videow ) { bool Event::WriteFrameVideo( const Image *image, const struct timeval timestamp, VideoWriter* videow ) {
@ -483,20 +483,23 @@ void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *
static char event_file[PATH_MAX]; static char event_file[PATH_MAX];
snprintf( event_file, sizeof(event_file), capture_file_format, path, frames ); snprintf( event_file, sizeof(event_file), capture_file_format, path, frames );
if ( monitor->GetOptSaveJPEGs() & 4) { if ( monitor->GetOptSaveJPEGs() & 4 ) {
//If this is the first frame, we should add a thumbnail to the event directory //If this is the first frame, we should add a thumbnail to the event directory
if(frames == 10){ if ( frames == 10 ) {
char snapshot_file[PATH_MAX]; char snapshot_file[PATH_MAX];
snprintf( snapshot_file, sizeof(snapshot_file), "%s/snapshot.jpg", path ); snprintf( snapshot_file, sizeof(snapshot_file), "%s/snapshot.jpg", path );
WriteFrameImage( image, timestamp, snapshot_file ); WriteFrameImage( image, timestamp, snapshot_file );
} }
} }
if( monitor->GetOptSaveJPEGs() & 1) { if ( monitor->GetOptSaveJPEGs() & 1 ) {
Debug( 1, "Writing capture frame %d", frames ); Debug( 1, "Writing capture frame %d to %s", frames, event_file );
WriteFrameImage( image, timestamp, event_file ); if ( ! WriteFrameImage( image, timestamp, event_file ) ) {
Error("Failed to write frame image");
}
} }
if ( videowriter != NULL ) { if ( videowriter != NULL ) {
WriteFrameVideo( image, timestamp, videowriter ); Debug(3, "Writing video");
WriteFrameVideo(image, timestamp, videowriter);
} }
struct DeltaTimeval delta_time; struct DeltaTimeval delta_time;
@ -535,7 +538,7 @@ void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *
exit( mysql_errno( &dbconn ) ); exit( mysql_errno( &dbconn ) );
} }
} }
} } // end if db_frame
end_time = timestamp; end_time = timestamp;
@ -551,8 +554,8 @@ void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *
snprintf( event_file, sizeof(event_file), analyse_file_format, path, frames ); snprintf( event_file, sizeof(event_file), analyse_file_format, path, frames );
Debug( 1, "Writing analysis frame %d", frames ); Debug( 1, "Writing analysis frame %d", frames );
if ( monitor->GetOptSaveJPEGs() & 2) { if ( monitor->GetOptSaveJPEGs() & 2 ) {
WriteFrameImage( alarm_image, timestamp, event_file, true ); WriteFrameImage(alarm_image, timestamp, event_file, true);
} }
} }
} }

View File

@ -832,6 +832,11 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
startTime, startTime,
this->getMonitor()); this->getMonitor());
} // end if record_audio } // end if record_audio
if ( ! videoStore->open() ) {
delete videoStore;
videoStore = NULL;
} else {
strcpy(oldDirectory, event_file); strcpy(oldDirectory, event_file);
monitor->SetVideoWriterEventId( last_event_id ); monitor->SetVideoWriterEventId( last_event_id );
@ -864,6 +869,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
delete queued_packet; delete queued_packet;
} // end while packets in the packetqueue } // end while packets in the packetqueue
Debug(2, "Wrote %d queued packets", packet_count ); Debug(2, "Wrote %d queued packets", packet_count );
}
} // end if ! was recording } // end if ! was recording
} else { } else {

View File

@ -940,33 +940,27 @@ cinfo->out_color_space = JCS_RGB;
// Multiple calling formats to permit inclusion (or not) of both quality_override and timestamp (exif), with suitable defaults. // Multiple calling formats to permit inclusion (or not) of both quality_override and timestamp (exif), with suitable defaults.
// Note quality=zero means default // Note quality=zero means default
bool Image::WriteJpeg( const char *filename, int quality_override) const bool Image::WriteJpeg(const char *filename, int quality_override) const {
{
return Image::WriteJpeg(filename, quality_override, (timeval){0,0}); return Image::WriteJpeg(filename, quality_override, (timeval){0,0});
} }
bool Image::WriteJpeg( const char *filename) const bool Image::WriteJpeg(const char *filename) const {
{
return Image::WriteJpeg(filename, 0, (timeval){0,0}); return Image::WriteJpeg(filename, 0, (timeval){0,0});
} }
bool Image::WriteJpeg( const char *filename, struct timeval timestamp ) const bool Image::WriteJpeg(const char *filename, struct timeval timestamp) const {
{ return Image::WriteJpeg(filename, 0, timestamp);
return Image::WriteJpeg(filename,0,timestamp);
} }
bool Image::WriteJpeg( const char *filename, int quality_override, struct timeval timestamp ) const bool Image::WriteJpeg( const char *filename, int quality_override, struct timeval timestamp ) const {
{ if ( config.colour_jpeg_files && colours == ZM_COLOUR_GRAY8 ) {
if ( config.colour_jpeg_files && colours == ZM_COLOUR_GRAY8 )
{
Image temp_image( *this ); Image temp_image( *this );
temp_image.Colourise( ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB ); temp_image.Colourise( ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB );
return( temp_image.WriteJpeg( filename, quality_override, timestamp) ); return temp_image.WriteJpeg(filename, quality_override, timestamp);
} }
int quality = quality_override?quality_override:config.jpeg_file_quality; int quality = quality_override?quality_override:config.jpeg_file_quality;
struct jpeg_compress_struct *cinfo = writejpg_ccinfo[quality]; struct jpeg_compress_struct *cinfo = writejpg_ccinfo[quality];
if ( !cinfo ) if ( !cinfo ) {
{
cinfo = writejpg_ccinfo[quality] = new jpeg_compress_struct; cinfo = writejpg_ccinfo[quality] = new jpeg_compress_struct;
cinfo->err = jpeg_std_error( &jpg_err.pub ); cinfo->err = jpeg_std_error( &jpg_err.pub );
jpg_err.pub.error_exit = zm_jpeg_error_exit; jpg_err.pub.error_exit = zm_jpeg_error_exit;
@ -975,8 +969,7 @@ bool Image::WriteJpeg( const char *filename, int quality_override, struct timeva
} }
FILE *outfile; FILE *outfile;
if ( (outfile = fopen( filename, "wb" )) == NULL ) if ( (outfile = fopen( filename, "wb" )) == NULL ) {
{
Error( "Can't open %s: %s", filename, strerror(errno) ); Error( "Can't open %s: %s", filename, strerror(errno) );
return( false ); return( false );
} }
@ -996,11 +989,11 @@ bool Image::WriteJpeg( const char *filename, int quality_override, struct timeva
{ {
#ifdef JCS_EXTENSIONS #ifdef JCS_EXTENSIONS
cinfo->input_components = 4; cinfo->input_components = 4;
if(subpixelorder == ZM_SUBPIX_ORDER_BGRA) { if ( subpixelorder == ZM_SUBPIX_ORDER_BGRA ) {
cinfo->in_color_space = JCS_EXT_BGRX; cinfo->in_color_space = JCS_EXT_BGRX;
} else if(subpixelorder == ZM_SUBPIX_ORDER_ARGB) { } else if ( subpixelorder == ZM_SUBPIX_ORDER_ARGB ) {
cinfo->in_color_space = JCS_EXT_XRGB; cinfo->in_color_space = JCS_EXT_XRGB;
} else if(subpixelorder == ZM_SUBPIX_ORDER_ABGR) { } else if ( subpixelorder == ZM_SUBPIX_ORDER_ABGR ) {
cinfo->in_color_space = JCS_EXT_XBGR; cinfo->in_color_space = JCS_EXT_XBGR;
} else { } else {
/* Assume RGBA */ /* Assume RGBA */
@ -1010,7 +1003,7 @@ bool Image::WriteJpeg( const char *filename, int quality_override, struct timeva
Error("libjpeg-turbo is required for JPEG encoding directly from RGB32 source"); Error("libjpeg-turbo is required for JPEG encoding directly from RGB32 source");
jpeg_abort_compress( cinfo ); jpeg_abort_compress( cinfo );
fclose(outfile); fclose(outfile);
return(false); return false;
#endif #endif
break; break;
} }
@ -1018,14 +1011,14 @@ bool Image::WriteJpeg( const char *filename, int quality_override, struct timeva
default: default:
{ {
cinfo->input_components = 3; cinfo->input_components = 3;
if(subpixelorder == ZM_SUBPIX_ORDER_BGR) { if ( subpixelorder == ZM_SUBPIX_ORDER_BGR) {
#ifdef JCS_EXTENSIONS #ifdef JCS_EXTENSIONS
cinfo->in_color_space = JCS_EXT_BGR; cinfo->in_color_space = JCS_EXT_BGR;
#else #else
Error("libjpeg-turbo is required for JPEG encoding directly from BGR24 source"); Error("libjpeg-turbo is required for JPEG encoding directly from BGR24 source");
jpeg_abort_compress( cinfo ); jpeg_abort_compress( cinfo );
fclose(outfile); fclose(outfile);
return(false); return false;
#endif #endif
} else { } else {
/* Assume RGB */ /* Assume RGB */
@ -1052,8 +1045,7 @@ cinfo->out_color_space = JCS_RGB;
} }
// If we have a non-zero time (meaning a parameter was passed in), then form a simple exif segment with that time as DateTimeOriginal and SubsecTimeOriginal // If we have a non-zero time (meaning a parameter was passed in), then form a simple exif segment with that time as DateTimeOriginal and SubsecTimeOriginal
// No timestamp just leave off the exif section. // No timestamp just leave off the exif section.
if(timestamp.tv_sec) if ( timestamp.tv_sec ) {
{
#define EXIFTIMES_MS_OFFSET 0x36 // three decimal digits for milliseconds #define EXIFTIMES_MS_OFFSET 0x36 // three decimal digits for milliseconds
#define EXIFTIMES_MS_LEN 0x03 #define EXIFTIMES_MS_LEN 0x03
#define EXIFTIMES_OFFSET 0x3E // 19 characters format '2015:07:21 13:14:45' not including quotes #define EXIFTIMES_OFFSET 0x3E // 19 characters format '2015:07:21 13:14:45' not including quotes
@ -1078,17 +1070,16 @@ cinfo->out_color_space = JCS_RGB;
JSAMPROW row_pointer; /* pointer to a single row */ JSAMPROW row_pointer; /* pointer to a single row */
int row_stride = cinfo->image_width * colours; /* physical row width in buffer */ int row_stride = cinfo->image_width * colours; /* physical row width in buffer */
while ( cinfo->next_scanline < cinfo->image_height ) while ( cinfo->next_scanline < cinfo->image_height ) {
{
row_pointer = &buffer[cinfo->next_scanline * row_stride]; row_pointer = &buffer[cinfo->next_scanline * row_stride];
jpeg_write_scanlines( cinfo, &row_pointer, 1 ); jpeg_write_scanlines( cinfo, &row_pointer, 1 );
} }
jpeg_finish_compress( cinfo ); jpeg_finish_compress(cinfo);
fclose( outfile ); fclose(outfile);
return( true ); return true;
} }
bool Image::DecodeJpeg( const JOCTET *inbuffer, int inbuffer_size, unsigned int p_colours, unsigned int p_subpixelorder) bool Image::DecodeJpeg( const JOCTET *inbuffer, int inbuffer_size, unsigned int p_colours, unsigned int p_subpixelorder)

View File

@ -193,6 +193,13 @@ void Logger::initialise( const std::string &id, const Options &options ) {
} }
} }
} // end foreach target } // end foreach target
} else {
// if we don't have debug turned on, then the max effective log level is INFO
if ( tempSyslogLevel > INFO ) tempSyslogLevel = INFO;
if ( tempFileLevel > INFO ) tempFileLevel = INFO;
if ( tempTermLevel > INFO ) tempTermLevel = INFO;
if ( tempDatabaseLevel > INFO ) tempDatabaseLevel = INFO;
if ( tempLevel > INFO ) tempLevel = INFO;
} // end if config.log_debug } // end if config.log_debug

View File

@ -35,6 +35,7 @@
#endif // ZM_HAS_V4L #endif // ZM_HAS_V4L
#include "zm_remote_camera.h" #include "zm_remote_camera.h"
#include "zm_remote_camera_http.h" #include "zm_remote_camera_http.h"
#include "zm_remote_camera_nvsocket.h"
#if HAVE_LIBAVFORMAT #if HAVE_LIBAVFORMAT
#include "zm_remote_camera_rtsp.h" #include "zm_remote_camera_rtsp.h"
#endif // HAVE_LIBAVFORMAT #endif // HAVE_LIBAVFORMAT
@ -2526,13 +2527,13 @@ Monitor *Monitor::Load( unsigned int p_id, bool load_zones, Purpose purpose ) {
unsigned int id = atoi(dbrow[col]); col++; unsigned int id = atoi(dbrow[col]); col++;
std::string name = dbrow[col]; col++; std::string name = dbrow[col]; col++;
unsigned int server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; unsigned int server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++;
unsigned int storage_id = atoi(dbrow[col]); col++; unsigned int storage_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++;
std::string type = dbrow[col]; col++; std::string type = dbrow[col]; col++;
int function = atoi(dbrow[col]); col++; int function = atoi(dbrow[col]); col++;
int enabled = atoi(dbrow[col]); col++; int enabled = atoi(dbrow[col]); col++;
std::string linked_monitors = dbrow[col] ? dbrow[col] : ""; col++; std::string linked_monitors = dbrow[col] ? dbrow[col] : ""; col++;
std::string device = dbrow[col]; col++; std::string device = dbrow[col] ? dbrow[col] : ""; col++;
int channel = atoi(dbrow[col]); col++; int channel = atoi(dbrow[col]); col++;
int format = atoi(dbrow[col]); col++; int format = atoi(dbrow[col]); col++;
@ -2642,6 +2643,22 @@ Monitor *Monitor::Load( unsigned int p_id, bool load_zones, Purpose purpose ) {
#else // ZM_HAS_V4L #else // ZM_HAS_V4L
Fatal( "You must have video4linux libraries and headers installed to use local analog or USB cameras for monitor %d", id ); Fatal( "You must have video4linux libraries and headers installed to use local analog or USB cameras for monitor %d", id );
#endif // ZM_HAS_V4L #endif // ZM_HAS_V4L
} else if ( type == "NVSocket" ) {
camera = new RemoteCameraNVSocket(
id,
host.c_str(),
port.c_str(),
path.c_str(),
width,
height,
colours,
brightness,
contrast,
hue,
colour,
purpose==CAPTURE,
record_audio
);
} else if ( type == "Remote" ) { } else if ( type == "Remote" ) {
if ( protocol == "http" ) { if ( protocol == "http" ) {
camera = new RemoteCameraHttp( camera = new RemoteCameraHttp(

View File

@ -94,3 +94,21 @@ void RemoteCamera::Initialise() {
Fatal( "Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret) ); Fatal( "Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret) );
} }
} }
int RemoteCamera::Read( int fd, char *buf, int size ) {
int ReceivedBytes = 0;
int bytes;
while ( ReceivedBytes < size ) {
// recv blocks until we get data, but it may be of ARBITRARY LENGTH and INCOMPLETE
int bytes_to_recv = size - ReceivedBytes;
if ( SOCKET_BUF_SIZE < bytes_to_recv )
bytes_to_recv = SOCKET_BUF_SIZE;
bytes = recv(fd, &buf[ReceivedBytes], bytes_to_recv, 0); //socket, buffer, len, flags
if ( bytes <= 0 ) {
Error("RemoteCamera::Read Recv error. Closing Socket\n");
return -1;
}
ReceivedBytes += bytes;
}
return ReceivedBytes;
}

View File

@ -29,12 +29,13 @@
#include <netdb.h> #include <netdb.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#define SOCKET_BUF_SIZE 8192
// //
// Class representing 'remote' cameras, i.e. those which are // Class representing 'remote' cameras, i.e. those which are
// accessed over a network connection. // accessed over a network connection.
// //
class RemoteCamera : public Camera class RemoteCamera : public Camera {
{
protected: protected:
std::string protocol; std::string protocol;
std::string host; std::string host;
@ -90,6 +91,7 @@ public:
virtual int Capture( Image &image ) = 0; virtual int Capture( Image &image ) = 0;
virtual int PostCapture() = 0; virtual int PostCapture() = 0;
virtual int CaptureAndRecord( Image &image, timeval recording, char* event_directory )=0; virtual int CaptureAndRecord( Image &image, timeval recording, char* event_directory )=0;
int Read( int fd, char*buf, int size );
}; };
#endif // ZM_REMOTE_CAMERA_H #endif // ZM_REMOTE_CAMERA_H

View File

@ -0,0 +1,308 @@
//
// ZoneMinder Remote Camera Class Implementation, $Date$, $Revision$
// Copyright (C) 2001-2008 Philip Coombes
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
#include "zm_remote_camera_nvsocket.h"
#include "zm_mem_utils.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <netdb.h>
#ifdef SOLARIS
#include <sys/filio.h> // FIONREAD and friends
#endif
#ifdef __FreeBSD__
#include <netinet/in.h>
#endif
RemoteCameraNVSocket::RemoteCameraNVSocket(
unsigned int p_monitor_id,
const std::string &p_host,
const std::string &p_port,
const std::string &p_path,
int p_width,
int p_height,
int p_colours,
int p_brightness,
int p_contrast,
int p_hue,
int p_colour,
bool p_capture,
bool p_record_audio ) :
RemoteCamera(
p_monitor_id,
"http",
p_host,
p_port,
p_path,
p_width,
p_height,
p_colours,
p_brightness,
p_contrast,
p_hue,
p_colour,
p_capture,
p_record_audio )
{
sd = -1;
timeout.tv_sec = 0;
timeout.tv_usec = 0;
if ( capture ) {
Initialise();
}
}
RemoteCameraNVSocket::~RemoteCameraNVSocket() {
if ( capture ) {
Terminate();
}
}
void RemoteCameraNVSocket::Initialise() {
RemoteCamera::Initialise();
if ( !timeout.tv_sec ) {
timeout.tv_sec = config.http_timeout/1000;
timeout.tv_usec = (config.http_timeout%1000)*1000;
}
int max_size = width*height*colours;
buffer.size( max_size );
mode = SINGLE_IMAGE;
format = UNDEF;
state = HEADER;
}
int RemoteCameraNVSocket::Connect() {
//struct addrinfo *p;
struct sockaddr_in servaddr;
bzero( &servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htons(INADDR_ANY);
servaddr.sin_port = htons(atoi(port.c_str()));
sd = socket(AF_INET, SOCK_STREAM, 0);
//for(p = hp; p != NULL; p = p->ai_next) {
//sd = socket( p->ai_family, p->ai_socktype, p->ai_protocol );
if ( sd < 0 ) {
Warning("Can't create socket: %s", strerror(errno) );
//continue;
return -1;
}
//if ( connect( sd, p->ai_addr, p->ai_addrlen ) < 0 ) {
if ( connect( sd, (struct sockaddr *)&servaddr , sizeof(servaddr) ) < 0 ) {
close(sd);
sd = -1;
Warning("Can't connect to socket mid: %d : %s", monitor_id, strerror(errno) );
return -1;
//continue;
//}
/* If we got here, we must have connected successfully */
//break;
}
//if ( p == NULL ) {
//Error("Unable to connect to the remote camera, aborting");
//return( -1 );
//}
Debug( 3, "Connected to host, socket = %d", sd );
return( sd );
}
int RemoteCameraNVSocket::Disconnect() {
close( sd );
sd = -1;
Debug( 3, "Disconnected from host" );
return( 0 );
}
int RemoteCameraNVSocket::SendRequest( std::string request ) {
Debug( 2, "Sending request: %s", request.c_str() );
if ( write( sd, request.data(), request.length() ) < 0 ) {
Error( "Can't write: %s", strerror(errno) );
Disconnect();
return( -1 );
}
Debug( 3, "Request sent" );
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 RemoteCameraNVSocket::ReadData( Buffer &buffer, unsigned int bytes_expected ) {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(sd, &rfds);
struct timeval temp_timeout = timeout;
int n_found = select(sd+1, &rfds, NULL, NULL, &temp_timeout);
if ( n_found == 0 ) {
Debug( 4, "Select timed out timeout was %d secs %d usecs", temp_timeout.tv_sec, temp_timeout.tv_usec );
int error = 0;
socklen_t len = sizeof(error);
int retval = getsockopt(sd, SOL_SOCKET, SO_ERROR, &error, &len);
if ( retval != 0 ) {
Debug(1, "error getting socket error code %s", strerror(retval));
}
if ( error != 0 ) {
return -1;
}
// Why are we disconnecting? It's just a timeout, meaning that data wasn't available.
//Disconnect();
return 0;
} else if ( n_found < 0 ) {
Error("Select error: %s", strerror(errno));
return -1;
}
unsigned int total_bytes_to_read = 0;
if ( bytes_expected ) {
total_bytes_to_read = bytes_expected;
} else {
if ( ioctl( sd, FIONREAD, &total_bytes_to_read ) < 0 ) {
Error( "Can't ioctl(): %s", strerror(errno) );
return( -1 );
}
if ( total_bytes_to_read == 0 ) {
if ( mode == SINGLE_IMAGE ) {
int error = 0;
socklen_t len = sizeof (error);
int retval = getsockopt( sd, SOL_SOCKET, SO_ERROR, &error, &len );
if(retval != 0 ) {
Debug( 1, "error getting socket error code %s", strerror(retval) );
}
if (error != 0) {
return -1;
}
// Case where we are grabbing a single jpg, but no content-length was given, so the expectation is that we read until close.
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 );
}
// There can be lots of bytes available. I've seen 4MB or more. This will vastly inflate our buffer size unnecessarily.
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 );
}
} // end if bytes_expected or not
Debug( 3, "Expecting %d bytes", total_bytes_to_read );
int total_bytes_read = 0;
do {
int bytes_read = buffer.read_into( sd, total_bytes_to_read );
if ( bytes_read < 0 ) {
Error( "Read error: %s", strerror(errno) );
return( -1 );
} else if ( bytes_read == 0 ) {
Debug( 2, "Socket closed" );
//Disconnect(); // Disconnect is done outside of ReadData now.
return( -1 );
} else if ( (unsigned int)bytes_read < total_bytes_to_read ) {
Error( "Incomplete read, expected %d, got %d", total_bytes_to_read, bytes_read );
return( -1 );
}
Debug( 3, "Read %d bytes", bytes_read );
total_bytes_read += bytes_read;
total_bytes_to_read -= bytes_read;
} while ( total_bytes_to_read );
Debug( 4, buffer );
return( total_bytes_read );
}
int RemoteCameraNVSocket::PreCapture() {
if ( sd < 0 ) {
Connect();
if ( sd < 0 ) {
Error( "Unable to connect to camera" );
return( -1 );
}
mode = SINGLE_IMAGE;
buffer.clear();
}
struct image_def {
uint16_t width;
uint16_t height;
uint16_t type;
};
struct image_def image_def;
if ( SendRequest("GetImageParams") < 0 ) {
Error( "Unable to send request" );
Disconnect();
return -1;
}
if ( Read(sd, (char *) &image_def, sizeof(image_def)) != sizeof(image_def) ) {
Error("Unable to GetImageParams");
Disconnect();
return -1;
}
if ( image_def.width != width || image_def.height != height ) {
Error("Incorrect width and height set. %dx%d != %dx%d", width, height, image_def.width, image_def.height );
Disconnect();
return -1;
}
return 0;
}
int RemoteCameraNVSocket::Capture( Image &image ) {
if ( SendRequest("GetNextImage") < 0 ) {
Warning( "Unable to capture image, retrying" );
return( 1 );
}
if ( Read( sd, buffer, imagesize ) < imagesize ) {
Warning( "Unable to capture image, retrying" );
return( 1 );
}
image.Assign( width, height, colours, subpixelorder, buffer, imagesize );
return( 0 );
}
int RemoteCameraNVSocket::PostCapture()
{
return( 0 );
}

View File

@ -0,0 +1,76 @@
//
// ZoneMinder Remote NVSOCKET Camera Class Interface, $Date$, $Revision$
// Copyright (C) 2017 ZoneMinder LLC
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
#ifndef ZM_REMOTE_CAMERA_NVSOCKET_H
#define ZM_REMOTE_CAMERA_NVSOCKET_H
#include "zm_remote_camera.h"
#include "zm_buffer.h"
#include "zm_regexp.h"
#include "zm_utils.h"
//
// Class representing 'http' cameras, i.e. those which are
// accessed over a network connection using http
//
class RemoteCameraNVSocket : public RemoteCamera {
protected:
std::string request;
struct timeval timeout;
//struct hostent *hp;
//struct sockaddr_in sa;
int sd;
Buffer buffer;
enum { SINGLE_IMAGE, MULTI_IMAGE } mode;
enum { UNDEF, JPEG, X_RGB, X_RGBZ } format;
enum { HEADER, HEADERCONT, SUBHEADER, SUBHEADERCONT, CONTENT } state;
enum { SIMPLE, REGEXP } method;
public:
RemoteCameraNVSocket(
unsigned int p_monitor_id,
const std::string &host,
const std::string &port,
const std::string &path,
int p_width,
int p_height,
int p_colours,
int p_brightness,
int p_contrast,
int p_hue,
int p_colour,
bool p_capture,
bool p_record_audio );
~RemoteCameraNVSocket();
void Initialise();
void Terminate() { Disconnect(); }
int Connect();
int Disconnect();
int SendRequest( std::string );
int ReadData( Buffer &buffer, unsigned int bytes_expected=0 );
int GetResponse();
int PreCapture();
int Capture( Image &image );
int PostCapture();
int CaptureAndRecord( Image &image, timeval recording, char* event_directory ) {return(0);};
};
#endif // ZM_REMOTE_CAMERA_NVSOCKET_H

View File

@ -125,7 +125,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
#else #else
video_out_stream = video_out_stream =
avformat_new_stream(oc, reinterpret_cast<const AVCodec *>(video_in_ctx->codec)); avformat_new_stream(oc,(const AVCodec *)(video_in_ctx->codec));
if (!video_out_stream) { if (!video_out_stream) {
Fatal("Unable to create video out stream\n"); Fatal("Unable to create video out stream\n");
} else { } else {
@ -222,7 +222,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
Debug(3, "Got AAC"); Debug(3, "Got AAC");
audio_out_stream = audio_out_stream =
avformat_new_stream(oc, reinterpret_cast<const AVCodec *>(audio_in_ctx->codec)); avformat_new_stream(oc, (const AVCodec *)(audio_in_ctx->codec));
if (!audio_out_stream) { if (!audio_out_stream) {
Error("Unable to create audio out stream\n"); Error("Unable to create audio out stream\n");
audio_out_stream = NULL; audio_out_stream = NULL;
@ -279,12 +279,25 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
} }
} // end if audio_in_stream } // end if audio_in_stream
video_last_pts = 0;
video_last_dts = 0;
audio_last_pts = 0;
audio_last_dts = 0;
video_next_pts = 0;
video_next_dts = 0;
audio_next_pts = 0;
audio_next_dts = 0;
} // VideoStore::VideoStore
bool VideoStore::open() {
/* open the out file, if needed */ /* open the out file, if needed */
if (!(out_format->flags & AVFMT_NOFILE)) { if (!(out_format->flags & AVFMT_NOFILE)) {
ret = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE, NULL, NULL); ret = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE, NULL, NULL);
if (ret < 0) { if (ret < 0) {
Fatal("Could not open out file '%s': %s\n", filename, Error("Could not open out file '%s': %s\n", filename,
av_make_error_string(ret).c_str()); av_make_error_string(ret).c_str());
return false;
} }
} }
@ -310,18 +323,11 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
if (ret < 0) { if (ret < 0) {
Error("Error occurred when writing out file header to %s: %s\n", Error("Error occurred when writing out file header to %s: %s\n",
filename, av_make_error_string(ret).c_str()); filename, av_make_error_string(ret).c_str());
return false;
} }
if (opts) av_dict_free(&opts); if (opts) av_dict_free(&opts);
return true;
video_last_pts = 0; }
video_last_dts = 0;
audio_last_pts = 0;
audio_last_dts = 0;
video_next_pts = 0;
video_next_dts = 0;
audio_next_pts = 0;
audio_next_dts = 0;
} // VideoStore::VideoStore
VideoStore::~VideoStore() { VideoStore::~VideoStore() {
if (audio_out_codec) { if (audio_out_codec) {

View File

@ -78,6 +78,7 @@ public:
AVStream *audio_in_stream, AVStream *audio_in_stream,
int64_t nStartTime, int64_t nStartTime,
Monitor * p_monitor); Monitor * p_monitor);
bool open();
~VideoStore(); ~VideoStore();
int writeVideoFramePacket( AVPacket *pkt ); int writeVideoFramePacket( AVPacket *pkt );

View File

@ -112,7 +112,7 @@ void Zone::Setup(
if ( config.record_diag_images ) { if ( config.record_diag_images ) {
static char diag_path[PATH_MAX] = ""; static char diag_path[PATH_MAX] = "";
if ( ! diag_path[0] ) { if ( ! diag_path[0] ) {
snprintf( diag_path, sizeof(diag_path), "%s/%s/diag-%d-poly.jpg", staticConfig.DIR_EVENTS.c_str(), monitor->Name(), id); snprintf( diag_path, sizeof(diag_path), "%s/diag-%d-poly.jpg", monitor->getStorage()->Path(), id);
} }
pg_image->WriteJpeg( diag_path ); pg_image->WriteJpeg( diag_path );
} }
@ -233,7 +233,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
if ( config.record_diag_images ) { if ( config.record_diag_images ) {
static char diag_path[PATH_MAX] = ""; static char diag_path[PATH_MAX] = "";
if ( ! diag_path[0] ) { if ( ! diag_path[0] ) {
snprintf( diag_path, sizeof(diag_path), "%s/%s/diag-%d-%d.jpg", staticConfig.DIR_EVENTS.c_str(), monitor->Name(), id, 1 ); snprintf( diag_path, sizeof(diag_path), "%s/diag-%d-%d.jpg", monitor->getStorage()->Path(), id, 1 );
} }
diff_image->WriteJpeg( diag_path ); diff_image->WriteJpeg( diag_path );
} }
@ -315,7 +315,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
if ( config.record_diag_images ) { if ( config.record_diag_images ) {
static char diag_path[PATH_MAX] = ""; static char diag_path[PATH_MAX] = "";
if ( !diag_path[0] ) { if ( !diag_path[0] ) {
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", staticConfig.DIR_EVENTS.c_str(), monitor->Id(), id, 2 ); snprintf( diag_path, sizeof(diag_path), "%s/diag-%d-%d.jpg", monitor->getStorage()->Path(), id, 2 );
} }
diff_image->WriteJpeg( diag_path ); diff_image->WriteJpeg( diag_path );
} }
@ -525,7 +525,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
if ( config.record_diag_images ) { if ( config.record_diag_images ) {
static char diag_path[PATH_MAX] = ""; static char diag_path[PATH_MAX] = "";
if ( !diag_path[0] ) { if ( !diag_path[0] ) {
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", staticConfig.DIR_EVENTS.c_str(), monitor->Id(), id, 3 ); snprintf( diag_path, sizeof(diag_path), "%s/diag-%d-%d.jpg", monitor->getStorage()->Path(), id, 3 );
} }
diff_image->WriteJpeg( diag_path ); diff_image->WriteJpeg( diag_path );
} }
@ -572,7 +572,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
if ( config.record_diag_images ) { if ( config.record_diag_images ) {
static char diag_path[PATH_MAX] = ""; static char diag_path[PATH_MAX] = "";
if ( !diag_path[0] ) { if ( !diag_path[0] ) {
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", staticConfig.DIR_EVENTS.c_str(), monitor->Id(), id, 4 ); snprintf( diag_path, sizeof(diag_path), "%s/diag-%d-%d.jpg", monitor->getStorage()->Path(), id, 4 );
} }
diff_image->WriteJpeg( diag_path ); diff_image->WriteJpeg( diag_path );
} }
@ -981,5 +981,5 @@ void Zone::std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsig
/* Store the results */ /* Store the results */
*pixel_count = pixelsalarmed; *pixel_count = pixelsalarmed;
*pixel_sum = pixelsdifference; *pixel_sum = pixelsdifference;
Debug( 7, "STORED"); Debug(7, "STORED pixelsalarmed(%d), pixelsdifference(%d)", pixelsalarmed, pixelsdifference);
} }

104
utils/generate_apache_config.pl Executable file
View File

@ -0,0 +1,104 @@
#!/usr/bin/perl -w
use strict;
use Getopt::Long ();
my $opts = {};
Getopt::Long::GetOptions($opts, 'help', 'output=s',
'pid_file=s', 'db_port=s', 'db_name=s', 'db_host=s', 'db_user=s', 'db_pass=s',
'min_port=s','max_port=s', 'debug=s',
'server_name=s','error_log=s','protocol=s',
);
if ($opts->{help}) {
usage();
exit 0;
} # end if
my %defaults = (
error_log => '/var/log/apache2/error.log',
output => '/etc/apache2/sites-available/zoneminder.conf',
protocol => 'http',
);
foreach my $key ( keys %defaults ) {
if ( ! $$opts{$key} ) {
$$opts{$key} = $defaults{$key};
}
}
my $Listen = '';
my $VirtualHostPorts;
if ( $$opts{protocol} eq 'https' ) {
if ( ! $$opts{server_name} ) {
die "https requires a server_name";
}
$VirtualHostPorts = ' *:443';
}
foreach my $port ( $$opts{min_port} .. $$opts{max_port} ) {
$Listen .= "Listen $port $$opts{protocol}\n";
$VirtualHostPorts .= " *:$port";
}
my $template =qq`
$Listen
<VirtualHost$VirtualHostPorts>
DocumentRoot /usr/share/zoneminder/www
`. ( $$opts{server_name} ? ' ServerName ' . $$opts{server_name} : '' ).
qq`
ErrorLog $$opts{error_log}
ScriptAlias /zm/cgi-bin/ /usr/lib/zoneminder/cgi-bin/
ScriptAlias /cgi-bin/ /usr/lib/zoneminder/cgi-bin/
<Directory "/usr/lib/zoneminder/cgi-bin">
AllowOverride None
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
Require all granted
Satisfy Any
Order allow,deny
Allow from all
</Directory>
Alias /zm /usr/share/zoneminder/www
<Directory /usr/share/zoneminder/www>
php_flag register_globals off
Options +Indexes +FollowSymLinks
AllowOverride All
<IfModule mod_dir.c>
DirectoryIndex index.php
</IfModule>
Require all granted
Satisfy Any
Order allow,deny
Allow from all
</Directory>
`;
if ( $$opts{protocol} eq 'https' ) {
$template .= qq`
SSLCertificateFile /etc/letsencrypt/live/$$opts{server_name}/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/$$opts{server_name}/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
`;
}
$template .= qq`
</VirtualHost>
`;
if ( open( F, "> $$opts{output}" ) ) {
binmode F;
print F $template;
close F;
} else {
die "Error opening $$opts{output}, Reason: $!";
} # end if
1;
__END__

View File

@ -1 +1 @@
1.31.3 1.31.7

View File

@ -368,7 +368,7 @@ function getNearEvents() {
else else
$midSql = ''; $midSql = '';
$sql = "select E.Id as Id from Events as E inner join Monitors as M on E.MonitorId = M.Id where ".dbEscape($sortColumn)." ".($sortOrder=='asc'?'<=':'>=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." order by $sortColumn ".($sortOrder=='asc'?'desc':'asc'); $sql = "select E.Id as Id, E.StartTime as StartTime from Events as E inner join Monitors as M on E.MonitorId = M.Id where $sortColumn ".($sortOrder=='asc'?'<=':'>=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." order by $sortColumn ".($sortOrder=='asc'?'desc':'asc');
$result = dbQuery( $sql ); $result = dbQuery( $sql );
while ( $id = dbFetchNext( $result, 'Id' ) ) { while ( $id = dbFetchNext( $result, 'Id' ) ) {
if ( $id == $eventId ) { if ( $id == $eventId ) {
@ -377,7 +377,7 @@ function getNearEvents() {
} }
} }
$sql = "select E.Id as Id from Events as E inner join Monitors as M on E.MonitorId = M.Id where $sortColumn ".($sortOrder=='asc'?'>=':'<=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." order by $sortColumn $sortOrder"; $sql = "select E.Id as Id, E.StartTime as StartTime from Events as E inner join Monitors as M on E.MonitorId = M.Id where $sortColumn ".($sortOrder=='asc'?'>=':'<=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." order by $sortColumn $sortOrder";
$result = dbQuery( $sql ); $result = dbQuery( $sql );
while ( $id = dbFetchNext( $result, 'Id' ) ) { while ( $id = dbFetchNext( $result, 'Id' ) ) {
if ( $id == $eventId ) { if ( $id == $eventId ) {
@ -389,6 +389,8 @@ function getNearEvents() {
$result = array( 'EventId'=>$eventId ); $result = array( 'EventId'=>$eventId );
$result['PrevEventId'] = empty($prevEvent)?0:$prevEvent['Id']; $result['PrevEventId'] = empty($prevEvent)?0:$prevEvent['Id'];
$result['NextEventId'] = empty($nextEvent)?0:$nextEvent['Id']; $result['NextEventId'] = empty($nextEvent)?0:$nextEvent['Id'];
$result['PrevEventStartTime'] = empty($prevEvent)?0:$prevEvent['StartTime'];
$result['NextEventStartTime'] = empty($nextEvent)?0:$nextEvent['StartTime'];
$result['PrevEventDefVideoPath'] = empty($prevEvent)?0:(getEventDefaultVideoPath($prevEvent)); $result['PrevEventDefVideoPath'] = empty($prevEvent)?0:(getEventDefaultVideoPath($prevEvent));
$result['NextEventDefVideoPath'] = empty($nextEvent)?0:(getEventDefaultVideoPath($nextEvent)); $result['NextEventDefVideoPath'] = empty($nextEvent)?0:(getEventDefaultVideoPath($nextEvent));
return( $result ); return( $result );

View File

@ -3,23 +3,19 @@
define( "MSG_TIMEOUT", ZM_WEB_AJAX_TIMEOUT ); define( "MSG_TIMEOUT", ZM_WEB_AJAX_TIMEOUT );
define( "MSG_DATA_SIZE", 4+256 ); define( "MSG_DATA_SIZE", 4+256 );
if ( !($_REQUEST['connkey'] && $_REQUEST['command']) ) if ( !($_REQUEST['connkey'] && $_REQUEST['command']) ) {
{
ajaxError( "Unexpected received message type '$type'" ); ajaxError( "Unexpected received message type '$type'" );
} }
if ( !($socket = @socket_create( AF_UNIX, SOCK_DGRAM, 0 )) ) if ( !($socket = @socket_create( AF_UNIX, SOCK_DGRAM, 0 )) ) {
{
ajaxError( "socket_create() failed: ".socket_strerror(socket_last_error()) ); ajaxError( "socket_create() failed: ".socket_strerror(socket_last_error()) );
} }
$locSockFile = ZM_PATH_SOCKS.'/zms-'.sprintf("%06d",$_REQUEST['connkey']).'w.sock'; $locSockFile = ZM_PATH_SOCKS.'/zms-'.sprintf("%06d",$_REQUEST['connkey']).'w.sock';
if ( !@socket_bind( $socket, $locSockFile ) ) if ( !@socket_bind( $socket, $locSockFile ) ) {
{
ajaxError( "socket_bind( $locSockFile ) failed: ".socket_strerror(socket_last_error()) ); ajaxError( "socket_bind( $locSockFile ) failed: ".socket_strerror(socket_last_error()) );
} }
switch ( $_REQUEST['command'] ) switch ( $_REQUEST['command'] ) {
{
case CMD_VARPLAY : case CMD_VARPLAY :
Logger::Debug( "Varplaying to ".$_REQUEST['rate'] ); Logger::Debug( "Varplaying to ".$_REQUEST['rate'] );
$msg = pack( "lcn", MSG_CMD, $_REQUEST['command'], $_REQUEST['rate']+32768 ); $msg = pack( "lcn", MSG_CMD, $_REQUEST['command'], $_REQUEST['rate']+32768 );
@ -64,26 +60,18 @@ $wSockets = NULL;
$eSockets = NULL; $eSockets = NULL;
$numSockets = @socket_select( $rSockets, $wSockets, $eSockets, intval(MSG_TIMEOUT/1000), (MSG_TIMEOUT%1000)*1000 ); $numSockets = @socket_select( $rSockets, $wSockets, $eSockets, intval(MSG_TIMEOUT/1000), (MSG_TIMEOUT%1000)*1000 );
if ( $numSockets === false ) if ( $numSockets === false ) {
{
ajaxError( "socket_select failed: ".socket_strerror(socket_last_error()) ); ajaxError( "socket_select failed: ".socket_strerror(socket_last_error()) );
} } else if ( $numSockets < 0 ) {
else if ( $numSockets < 0 )
{
ajaxError( "Socket closed $remSockFile" ); ajaxError( "Socket closed $remSockFile" );
} } else if ( $numSockets == 0 ) {
else if ( $numSockets == 0 )
{
ajaxError( "Timed out waiting for msg $remSockFile" ); ajaxError( "Timed out waiting for msg $remSockFile" );
} } else if ( $numSockets > 0 ) {
else if ( $numSockets > 0 )
{
if ( count($rSockets) != 1 ) if ( count($rSockets) != 1 )
ajaxError( "Bogus return from select, ".count($rSockets)." sockets available" ); ajaxError( "Bogus return from select, ".count($rSockets)." sockets available" );
} }
switch( $nbytes = @socket_recvfrom( $socket, $msg, MSG_DATA_SIZE, 0, $remSockFile ) ) switch( $nbytes = @socket_recvfrom( $socket, $msg, MSG_DATA_SIZE, 0, $remSockFile ) ) {
{
case -1 : case -1 :
{ {
ajaxError( "socket_recvfrom( $remSockFile ) failed: ".socket_strerror(socket_last_error()) ); ajaxError( "socket_recvfrom( $remSockFile ) failed: ".socket_strerror(socket_last_error()) );
@ -103,8 +91,7 @@ switch( $nbytes = @socket_recvfrom( $socket, $msg, MSG_DATA_SIZE, 0, $remSockFil
} }
$data = unpack( "ltype", $msg ); $data = unpack( "ltype", $msg );
switch ( $data['type'] ) switch ( $data['type'] ) {
{
case MSG_DATA_WATCH : case MSG_DATA_WATCH :
{ {
$data = unpack( "ltype/imonitor/istate/dfps/ilevel/irate/ddelay/izoom/Cdelayed/Cpaused/Cenabled/Cforced", $msg ); $data = unpack( "ltype/imonitor/istate/dfps/ilevel/irate/ddelay/izoom/Cdelayed/Cpaused/Cenabled/Cforced", $msg );
@ -150,8 +137,7 @@ switch ( $data['type'] )
ajaxError( 'Unrecognised action or insufficient permissions' ); ajaxError( 'Unrecognised action or insufficient permissions' );
function ajaxCleanup() function ajaxCleanup() {
{
global $socket, $locSockFile; global $socket, $locSockFile;
if ( !empty( $socket ) ) if ( !empty( $socket ) )
@socket_close( $socket ); @socket_close( $socket );

View File

@ -1,33 +0,0 @@
# Define the line ending behavior of the different file extensions
# Set default behaviour, in case users don't have core.autocrlf set.
* text=auto
# Explicitly declare text files we want to always be normalized and converted
# to native line endings on checkout.
*.php text
*.default text
*.ctp text
*.sql text
*.md text
*.po text
*.js text
*.css text
*.ini text
*.properties text
*.txt text
*.xml text
*.yml text
.htaccess text
# Declare files that will always have CRLF line endings on checkout.
*.bat eol=crlf
# Declare files that will always have LF line endings on checkout.
*.pem eol=lf
# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary
*.gif binary
*.ico binary
*.mo binary

21
web/api/.gitignore vendored
View File

@ -1,21 +0,0 @@
# User specific & automatically generated files #
#################################################
/app/Config/database.php
/app/tmp
/lib/Cake/Console/Templates/skel/tmp/
/plugins
/vendors
/build
/dist
/tags
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
Icon?
ehthumbs.db
Thumbs.db

176
web/includes/Group.php Normal file
View File

@ -0,0 +1,176 @@
<?php
class Group {
public $defaults = array(
'Id' => null,
'Name' => '',
'ParentId' => null,
'MonitorIds' => '',
);
public function __construct( $IdOrRow=NULL ) {
$row = NULL;
if ( $IdOrRow ) {
if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) {
$row = dbFetchOne( 'SELECT * FROM Groups WHERE Id=?', NULL, array( $IdOrRow ) );
if ( ! $row ) {
Error('Unable to load Group record for Id=' . $IdOrRow );
}
} elseif ( is_array( $IdOrRow ) ) {
$row = $IdOrRow;
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Error("Unknown argument passed to Group Constructor from $file:$line)");
Error("Unknown argument passed to Group Constructor ($IdOrRow)");
return;
}
} # end if isset($IdOrRow)
if ( $row ) {
foreach ($row as $k => $v) {
$this->{$k} = $v;
}
}
} // end function __construct
public function __call( $fn, array $args ) {
if ( count( $args ) ) {
$this->{$fn} = $args[0];
}
if ( array_key_exists( $fn, $this ) ) {
return $this->{$fn};
} else if ( array_key_exists( $fn, $this->defaults ) ) {
$this->{$fn} = $this->defaults{$fn};
return $this->{$fn};
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Warning( "Unknown function call Group->$fn from $file:$line" );
}
}
public static function find_all( $parameters = null ) {
$filters = array();
$sql = 'SELECT * FROM Groups ';
$values = array();
if ( $parameters ) {
$fields = array();
$sql .= 'WHERE ';
foreach ( $parameters as $field => $value ) {
if ( $value == null ) {
$fields[] = $field.' IS NULL';
} else if ( is_array( $value ) ) {
$func = function(){return '?';};
$fields[] = $field.' IN ('.implode(',', array_map( $func, $value ) ). ')';
$values += $value;
} else {
$fields[] = $field.'=?';
$values[] = $value;
}
}
$sql .= implode(' AND ', $fields );
}
$sql .= ' ORDER BY Name';
$result = dbQuery($sql, $values);
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Group');
foreach ( $results as $row => $obj ) {
$filters[] = $obj;
}
return $filters;
}
public function delete() {
dbQuery( 'DELETE FROM Groups WHERE Id = ?', array($this->{'Id'}) );
} # end function delete()
public function set( $data ) {
foreach ($data as $k => $v) {
if ( is_array( $v ) ) {
$this->{$k} = $v;
} else if ( is_string( $v ) ) {
$this->{$k} = trim( $v );
} else if ( is_integer( $v ) ) {
$this->{$k} = $v;
} else if ( is_bool( $v ) ) {
$this->{$k} = $v;
} else {
Error( "Unknown type $k => $v of var " . gettype( $v ) );
$this->{$k} = $v;
}
}
}
public function depth( $new = null ) {
if ( isset($new) ) {
$this->{'depth'} = $new;
}
if ( ! array_key_exists( 'depth', $this ) or ( $this->{'depth'} == null ) ) {
$this->{'depth'} = 1;
if ( $this->{'ParentId'} != null ) {
$Parent = new Group( $this->{'ParentId'} );
$this->{'depth'} += $Parent->depth();
}
}
return $this->{'depth'};
} // end public function depth
public static function get_group_dropdowns() {
# This will end up with the group_id of the deepest selection
$group_id = 0;
$depth = 0;
$groups = array();
$parent_group_ids = null;
while(1) {
$Groups = Group::find_all( array('ParentId'=>$parent_group_ids) );
if ( ! count( $Groups ) )
break;
$parent_group_ids = array();
$selected_group_id = 0;
if ( isset($_REQUEST['group'.$depth]) and $_REQUEST['group'.$depth] > 0 ) {
$selected_group_id = $group_id = $_REQUEST['group'.$depth];
} else if ( isset($_COOKIE['zmGroup'.$depth] ) and $_COOKIE['zmGroup'.$depth] > 0 ) {
$selected_group_id = $group_id = $_COOKIE['zmGroup'.$depth];
}
foreach ( $Groups as $Group ) {
if ( ! isset( $groups[$depth] ) ) {
$groups[$depth] = array(0=>'All');
}
$groups[$depth][$Group->Id()] = $Group->Name();
if ( $selected_group_id and ( $selected_group_id == $Group->Id() ) )
$parent_group_ids[] = $Group->Id();
}
echo htmlSelect( 'group'.$depth, $groups[$depth], $selected_group_id, "changeGroup(this,$depth);" );
if ( ! count($parent_group_ids) ) break;
$depth += 1;
}
return $group_id;
} # end public static function get_group_dropdowns()
public static function get_group_sql( $group_id ) {
$groupSql = '';
if ( $group_id ) {
if ( $group = dbFetchOne( 'SELECT MonitorIds FROM Groups WHERE Id=?', NULL, array($group_id) ) ) {
$groupIds = array();
if ( $group['MonitorIds'] )
$groupIds = explode( ',', $group['MonitorIds'] );
foreach ( dbFetchAll( 'SELECT MonitorIds FROM Groups WHERE ParentId = ?', NULL, array($group_id) ) as $group )
if ( $group['MonitorIds'] )
$groupIds = array_merge( $groupIds, explode( ',', $group['MonitorIds'] ) );
}
$groupSql = " find_in_set( Id, '".implode( ',', $groupIds )."' )";
}
return $groupSql;
} # end public static function get_group_sql( $group_id )
} # end class Group
?>

View File

@ -174,10 +174,13 @@ private $control_fields = array(
public function getStreamSrc( $args, $querySep='&amp;' ) { public function getStreamSrc( $args, $querySep='&amp;' ) {
if ( isset($this->{'ServerId'}) and $this->{'ServerId'} ) { if ( isset($this->{'ServerId'}) and $this->{'ServerId'} ) {
$Server = new Server( $this->{'ServerId'} ); $Server = new Server( $this->{'ServerId'} );
$streamSrc = ZM_BASE_PROTOCOL.'://'.$Server->Hostname().ZM_PATH_ZMS; $streamSrc = ZM_BASE_PROTOCOL.'://'.$Server->Hostname();
} else { } else {
$streamSrc = ZM_BASE_URL.ZM_PATH_ZMS; $streamSrc = ZM_BASE_URL;
} }
if ( ZM_MIN_STREAMING_PORT )
$streamSrc .= ':'. (ZM_MIN_STREAMING_PORT+$this->{'Id'});
$streamSrc .= ZM_PATH_ZMS;
$args['monitor'] = $this->{'Id'}; $args['monitor'] = $this->{'Id'};

View File

@ -605,11 +605,13 @@ Warning("Addterm");
if ( canEdit( 'Groups' ) ) { if ( canEdit( 'Groups' ) ) {
if ( $action == 'group' ) { if ( $action == 'group' ) {
# Should probably verfy that each monitor id is a valid monitor, that we have access to. HOwever at the moment, you have to have System permissions to do this # Should probably verfy that each monitor id is a valid monitor, that we have access to. HOwever at the moment, you have to have System permissions to do this
$monitors = empty( $_POST['newGroup']['MonitorIds'] ) ? NULL : implode(',', $_POST['newGroup']['MonitorIds']); $monitors = empty( $_POST['newGroup']['MonitorIds'] ) ? '' : implode(',', $_POST['newGroup']['MonitorIds'] );
if ( !empty($_POST['gid']) ) { if ( !empty($_POST['gid']) ) {
dbQuery( "UPDATE Groups SET Name=?, MonitorIds=? WHERE Id=?", array($_POST['newGroup']['Name'], $monitors, $_POST['gid']) ); dbQuery( 'UPDATE Groups SET Name=?, ParentId=?, MonitorIds=? WHERE Id=?',
array($_POST['newGroup']['Name'], ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), $monitors, $_POST['gid']) );
} else { } else {
dbQuery( "INSERT INTO Groups SET Name=?, MonitorIds=?", array( $_POST['newGroup']['Name'], $monitors ) ); dbQuery( 'INSERT INTO Groups SET Name=?, ParentId=?, MonitorIds=?',
array( $_POST['newGroup']['Name'], ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), $monitors ) );
} }
$view = 'none'; $view = 'none';
} }

View File

@ -46,11 +46,14 @@ $configSubFolder = ZM_CONFIG_SUBDIR;
if ( is_dir($configSubFolder) ) { if ( is_dir($configSubFolder) ) {
if ( is_readable($configSubFolder) ) { if ( is_readable($configSubFolder) ) {
foreach ( glob("$configSubFolder/*.conf") as $filename ) { foreach ( glob("$configSubFolder/*.conf") as $filename ) {
error_log("processing $filename");
$configvals = array_replace($configvals, process_configfile($filename) ); $configvals = array_replace($configvals, process_configfile($filename) );
} }
} else { } else {
error_log( "WARNING: ZoneMinder configuration subfolder found but is not readable. Check folder permissions on $configSubFolder." ); error_log( "WARNING: ZoneMinder configuration subfolder found but is not readable. Check folder permissions on $configSubFolder." );
} }
} else {
error_log( "WARNING: ZoneMinder configuration subfolder found but is not a directory. Check $configSubFolder." );
} }
# Now that our array our finalized, define each key => value # Now that our array our finalized, define each key => value

View File

@ -127,6 +127,10 @@ function dbQuery( $sql, $params=NULL ) {
} else { } else {
$result = $dbConn->query( $sql ); $result = $dbConn->query( $sql );
} }
//if ( $params )
//Warning("SQL: $sql" . implode(',',$params));
//else
//Warning("SQL: $sql" );
} catch(PDOException $e) { } catch(PDOException $e) {
Fatal( "SQL-ERR '".$e->getMessage()."', statement was '".$sql."'" ); Fatal( "SQL-ERR '".$e->getMessage()."', statement was '".$sql."'" );
} }

View File

@ -538,7 +538,7 @@ function makePopupButton( $url, $winName, $winSize, $buttonValue, $condition=1,
$popupParms = "'".$url."', '".$winName."', '".$winSize[0]."', ".$winSize[1].", ".$winSize[2]; $popupParms = "'".$url."', '".$winName."', '".$winSize[0]."', ".$winSize[1].", ".$winSize[2];
else else
$popupParms = "'".$url."', '".$winName."', '".$winSize."'"; $popupParms = "'".$url."', '".$winName."', '".$winSize."'";
$string = '<input class="btn btn-primary" type="button" value="'.$buttonValue.'" onclick="createPopup( '.$popupParms.' ); return( false );"'.($condition?'':' disabled="disabled"').($options?(' '.$options):'').'/>'; $string = '<input type="button" value="'.$buttonValue.'" onclick="createPopup( '.$popupParms.' ); return( false );"'.($condition?'':' disabled="disabled"').($options?(' '.$options):'').'/>';
return( $string ); return( $string );
} }
@ -557,6 +557,9 @@ function htmlSelect( $name, $contents, $values, $behaviours=false ) {
$html = "<select name=\"$name\" id=\"$name\"$behaviourText>"; $html = "<select name=\"$name\" id=\"$name\"$behaviourText>";
foreach ( $contents as $value=>$text ) { foreach ( $contents as $value=>$text ) {
//for ( $i = 0; $i < count($contents); $i +=2 ) {
//$value = $contents[$i];
//$text = $contents[$i+1];
$selected = is_array( $values ) ? in_array( $value, $values ) : $value==$values; $selected = is_array( $values ) ? in_array( $value, $values ) : $value==$values;
$html .= "<option value=\"$value\"".($selected?" selected=\"selected\"":'').">$text</option>"; $html .= "<option value=\"$value\"".($selected?" selected=\"selected\"":'').">$text</option>";
} }

View File

@ -48,6 +48,7 @@ require_once( 'includes/logger.php' );
require_once( 'includes/Server.php' ); require_once( 'includes/Server.php' );
require_once( 'includes/Storage.php' ); require_once( 'includes/Storage.php' );
require_once( 'includes/Event.php' ); require_once( 'includes/Event.php' );
require_once( 'includes/Group.php' );
require_once( 'includes/Monitor.php' ); require_once( 'includes/Monitor.php' );
if ( isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ) { if ( isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ) {
@ -194,7 +195,7 @@ isset($view) || $view = NULL;
isset($request) || $request = NULL; isset($request) || $request = NULL;
isset($action) || $action = NULL; isset($action) || $action = NULL;
if ( ZM_ENABLE_CSRF_MAGIC && $action != 'login' && $view != 'view_video' && $view != 'video' && $request != 'control' ) { if ( ZM_ENABLE_CSRF_MAGIC && $action != 'login' && $view != 'view_video' && $view != 'video' && $request != 'control' && $view != 'frames') {
require_once( 'includes/csrf/csrf-magic.php' ); require_once( 'includes/csrf/csrf-magic.php' );
Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\""); Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\"");
csrf_check(); csrf_check();

View File

@ -88,6 +88,7 @@ $SLANG = array(
'AddNewControl' => 'Add New Control', 'AddNewControl' => 'Add New Control',
'AddNewMonitor' => 'Add New Monitor', 'AddNewMonitor' => 'Add New Monitor',
'AddNewServer' => 'Add New Server', 'AddNewServer' => 'Add New Server',
'AddNewStorage' => 'Add New Storage',
'AddNewUser' => 'Add New User', 'AddNewUser' => 'Add New User',
'AddNewZone' => 'Add New Zone', 'AddNewZone' => 'Add New Zone',
'Alarm' => 'Alarm', 'Alarm' => 'Alarm',
@ -594,13 +595,13 @@ $SLANG = array(
'Record' => 'Record', 'Record' => 'Record',
'RefImageBlendPct' => 'Reference Image Blend %ge', 'RefImageBlendPct' => 'Reference Image Blend %ge',
'Refresh' => 'Refresh', 'Refresh' => 'Refresh',
'RemoteHostName' => 'Remote Host Name', 'RemoteHostName' => 'Host Name',
'RemoteHostPath' => 'Remote Host Path', 'RemoteHostPath' => 'Path',
'RemoteHostSubPath' => 'Remote Host SubPath', 'RemoteHostSubPath' => 'SubPath',
'RemoteHostPort' => 'Remote Host Port', 'RemoteHostPort' => 'Port',
'RemoteImageColours' => 'Remote Image Colours', 'RemoteImageColours' => 'Image Colours',
'RemoteMethod' => 'Remote Method', 'RemoteMethod' => 'Method',
'RemoteProtocol' => 'Remote Protocol', 'RemoteProtocol' => 'Protocol',
'Remote' => 'Remote', 'Remote' => 'Remote',
'Rename' => 'Rename', 'Rename' => 'Rename',
'ReplayAll' => 'All Events', 'ReplayAll' => 'All Events',
@ -644,8 +645,8 @@ $SLANG = array(
'ShowTimeline' => 'Show Timeline', 'ShowTimeline' => 'Show Timeline',
'SignalCheckColour' => 'Signal Check Colour', 'SignalCheckColour' => 'Signal Check Colour',
'Size' => 'Size', 'Size' => 'Size',
'SkinDescription' => 'Change the default skin for this computer', 'SkinDescription' => 'Change the skin for this session',
'CSSDescription' => 'Change the default css for this computer', 'CSSDescription' => 'Change the css for this session',
'Sleep' => 'Sleep', 'Sleep' => 'Sleep',
'SortAsc' => 'Asc', 'SortAsc' => 'Asc',
'SortBy' => 'Sort by', 'SortBy' => 'Sort by',

View File

@ -106,32 +106,32 @@
#monitorStatus #monitorState { #monitorStatus #monitorState {
} }
#dvrControls { .dvrControls {
margin-top: 3px; margin-top: 3px;
margin-bottom: 2px; margin-bottom: 2px;
text-align: center; text-align: center;
} }
#dvrControls input { .dvrControls input {
height: 20px; height: 20px;
width: 28px; width: 28px;
padding-bottom: 3px; padding-bottom: 3px;
margin: 0 3px; margin: 0 3px;
} }
#dvrControls input[disabled=disabled] { .dvrControls input[disabled=disabled] {
color: #aaaaaa; color: #aaaaaa;
} }
#dvrControls input.active { .dvrControls input.active {
border: 1px solid blue; border: 1px solid blue;
} }
#dvrControls input.inactive { .dvrControls input.inactive {
border: 1px solid green; border: 1px solid green;
} }
#dvrControls input.unavail { .dvrControls input.unavail {
border: 1px solid red; border: 1px solid red;
} }

View File

@ -33,3 +33,6 @@
width:100%; width:100%;
height:100%; height:100%;
} }
input[type=range]::-ms-tooltip {
display: none;
}

View File

@ -492,16 +492,13 @@ input[type=submit]:disabled {
#consoleTable td, #consoleTable th { #consoleTable td, #consoleTable th {
padding: 5px; padding: 5px;
border-bottom: 2px solid #f2f2f2; border-bottom: 1px solid #000000;
} }
#consoleTable input[type=button] { #consoleTable input[type=button] {
margin-right: 3px !important; margin-right: 3px !important;
} }
#consoleTable tfoot {
background-color: #f2f2f2;
}
.navbar{ .navbar{
margin-bottom: 0 !important; margin-bottom: 0 !important;
@ -537,3 +534,8 @@ input[type=submit]:disabled {
margin-left: 0; margin-left: 0;
} }
.table-striped > tbody > tr:nth-of-type(2n+1) {
background: none;
}

View File

@ -89,32 +89,32 @@
#monitorStatus #monitorState { #monitorStatus #monitorState {
} }
#dvrControls { .dvrControls {
margin-top: 3px; margin-top: 3px;
margin-bottom: 2px; margin-bottom: 2px;
text-align: center; text-align: center;
} }
#dvrControls input { .dvrControls input {
height: 20px; height: 20px;
width: 28px; width: 28px;
padding-bottom: 3px; padding-bottom: 3px;
margin: 0 3px; margin: 0 3px;
} }
#dvrControls input[disabled=disabled] { .dvrControls input[disabled=disabled] {
color: #aaaaaa; color: #aaaaaa;
} }
#dvrControls input.active { .dvrControls input.active {
border: 1px solid blue; border: 1px solid blue;
} }
#dvrControls input.inactive { .dvrControls input.inactive {
border: 1px solid green; border: 1px solid green;
} }
#dvrControls input.unavail { .dvrControls input.unavail {
border: 1px solid red; border: 1px solid red;
} }

View File

@ -33,3 +33,6 @@
width:100%; width:100%;
height:100%; height:100%;
} }
input[type=range]::-ms-tooltip {
display: none;
}

View File

@ -94,17 +94,18 @@ label {
input,textarea,select,button { input,textarea,select,button {
border: 1px #ccc solid; border: 1px #ccc solid;
padding: 10px; padding: 5px;
border-radius: 2px; border-radius: 2px;
font-family: inherit; font-family: inherit;
font-weight: 400; font-weight: 400;
font-size: 100%; font-size: 100%;
color: #333333; color: #333333;
} }
/*
input[type=text], input[type=password], input[type="url"], textarea, select { input[type=text], input[type=password], input[type="url"], textarea, select {
padding: 1px; padding: 1px;
} }
*/
input.noborder { input.noborder {
border: 0; border: 0;

View File

@ -98,36 +98,36 @@
#monitorStatus #monitorState { #monitorStatus #monitorState {
} }
#dvrControls { .dvrControls {
margin-top: 3px; margin-top: 3px;
margin-bottom: 2px; margin-bottom: 2px;
text-align: center; text-align: center;
} }
#dvrControls input { .dvrControls input {
padding: 10px 10px; padding: 10px 10px;
width: 50px; width: 50px;
margin: 0 3px; margin: 0 3px;
font-weight: 900; font-weight: 900;
} }
#dvrControls input[disabled=disabled] { .dvrControls input[disabled=disabled] {
color: #aaaaaa; color: #aaaaaa;
} }
#dvrControls input.active { .dvrControls input.active {
border: 0; border: 0;
background-color: #2ecc71; background-color: #2ecc71;
color: #fff; color: #fff;
} }
#dvrControls input.inactive { .dvrControls input.inactive {
border: 0; border: 0;
background-color: #e67e22; background-color: #e67e22;
color: #fff; color: #fff;
} }
#dvrControls input.unavail { .dvrControls input.unavail {
background-color: #ccc; background-color: #ccc;
border: 0; border: 0;
cursor: default; cursor: default;

View File

@ -10,6 +10,9 @@
#SpeedDiv label { #SpeedDiv label {
margin: 0; margin: 0;
} }
#DateTimeDiv {
display: inline-flex;
}
#scaleslideroutput, #scaleslideroutput,
#speedslideroutput { #speedslideroutput {
@ -29,7 +32,9 @@
#monitors { #monitors {
position:relative; position:relative;
background-color:black;
width:100%; width:100%;
height:100%; height:100%;
} }
input[type=range]::-ms-tooltip {
display: none;
}

View File

@ -46,8 +46,15 @@ function xhtmlHeaders( $file, $title ) {
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<title><?php echo ZM_WEB_TITLE_PREFIX ?> - <?php echo validHtmlStr($title) ?></title> <title><?php echo ZM_WEB_TITLE_PREFIX ?> - <?php echo validHtmlStr($title) ?></title>
<link rel="icon" type="image/ico" href="graphics/favicon.ico"/> <?php
<link rel="shortcut icon" href="graphics/favicon.ico"/> if ( file_exists( "skins/$skin/css/$css/graphics/favicon.ico" ) ) {
echo "<link rel=\"icon\" type=\"image/ico\" href=\"skins/$skin/css/$css/graphics/favicon.ico\"/>";
echo "<link rel=\"shortcut icon\" href=\"skins/$skin/css/$css/graphics/favicon.ico\"/>";
} else {
echo '<link rel="icon" type="image/ico" href="graphics/favicon.ico"/>';
echo '<link rel="shortcut icon" href="graphics/favicon.ico"/>';
}
?>
<link rel="stylesheet" href="css/reset.css" type="text/css"/> <link rel="stylesheet" href="css/reset.css" type="text/css"/>
<link rel="stylesheet" href="css/overlay.css" type="text/css"/> <link rel="stylesheet" href="css/overlay.css" type="text/css"/>
<link rel="stylesheet" href="css/bootstrap.min.css" type="text/css"/> <link rel="stylesheet" href="css/bootstrap.min.css" type="text/css"/>
@ -128,6 +135,10 @@ function xhtmlHeaders( $file, $title ) {
if ( $cssJsFile ) { if ( $cssJsFile ) {
?> ?>
<script type="text/javascript" src="<?php echo $cssJsFile ?>"></script> <script type="text/javascript" src="<?php echo $cssJsFile ?>"></script>
<?php
} else {
?>
<script type="text/javascript" src="skins/classic/js/classic.js"></script>
<?php } ?> <?php } ?>
<script type="text/javascript" src="<?php echo $skinJsFile ?>"></script> <script type="text/javascript" src="<?php echo $skinJsFile ?>"></script>
<script type="text/javascript" src="js/logger.js"></script> <script type="text/javascript" src="js/logger.js"></script>
@ -144,53 +155,24 @@ function xhtmlHeaders( $file, $title ) {
function getNavBarHTML() { function getNavBarHTML() {
$group = NULL;
if ( ! empty($_COOKIE['zmGroup']) ) {
if ( $group = dbFetchOne( 'select * from Groups where Id = ?', NULL, array($_COOKIE['zmGroup'])) )
$groupIds = array_flip(explode( ',', $group['MonitorIds'] ));
}
$maxWidth = 0;
$maxHeight = 0;
# Used to determine if the Cycle button should be made available
$cycleCount = 0;
$monitors = dbFetchAll( "select * from Monitors order by Sequence asc" );
global $displayMonitors;
$displayMonitors = array();
for ( $i = 0; $i < count($monitors); $i++ ) {
if ( !visibleMonitor( $monitors[$i]['Id'] ) ) {
continue;
}
if ( $group && !empty($groupIds) && !array_key_exists( $monitors[$i]['Id'], $groupIds ) ) {
continue;
}
if ( $monitors[$i]['Function'] != 'None' ) {
$cycleCount++;
$scaleWidth = reScale( $monitors[$i]['Width'], $monitors[$i]['DefaultScale'], ZM_WEB_DEFAULT_SCALE );
$scaleHeight = reScale( $monitors[$i]['Height'], $monitors[$i]['DefaultScale'], ZM_WEB_DEFAULT_SCALE );
if ( $maxWidth < $scaleWidth ) $maxWidth = $scaleWidth;
if ( $maxHeight < $scaleHeight ) $maxHeight = $scaleHeight;
}
$displayMonitors[] = $monitors[$i];
}
$cycleWidth = $maxWidth;
$cycleHeight = $maxHeight;
$versionClass = (ZM_DYN_DB_VERSION&&(ZM_DYN_DB_VERSION!=ZM_VERSION))?'errorText':''; $versionClass = (ZM_DYN_DB_VERSION&&(ZM_DYN_DB_VERSION!=ZM_VERSION))?'errorText':'';
ob_start(); ob_start();
global $CLANG;
global $VLANG;
global $CLANG;
global $VLANG;
global $running; global $running;
if ( $running == null ) if ( $running == null )
$running = daemonCheck(); $running = daemonCheck();
$status = $running?translate('Running'):translate('Stopped'); $status = $running?translate('Running'):translate('Stopped');
global $user; global $user;
global $bwArray; global $bwArray;
global $view;
?> ?>
<noscript>
<div style="background-color:red;color:white;font-size:x-large;">
ZoneMinder requires Javascript. Please enable Javascript in your browser for this site.
</div>
</noscript>
<div class="navbar navbar-inverse navbar-static-top"> <div class="navbar navbar-inverse navbar-static-top">
<div class="container-fluid"> <div class="container-fluid">
<div class="navbar-header"> <div class="navbar-header">
@ -213,20 +195,19 @@ function getNavBarHTML() {
<?php if ( ZM_OPT_X10 && canView( 'Devices' ) ) { ?> <?php if ( ZM_OPT_X10 && canView( 'Devices' ) ) { ?>
<li><a href="?view=devices">Devices</a></li> <li><a href="?view=devices">Devices</a></li>
<?php } ?> <?php } ?>
<li><?php echo makePopupLink( '?view=groups', 'zmGroups', 'groups', sprintf( $CLANG['MonitorCount'], count($displayMonitors), zmVlang( $VLANG['Monitor'], count($displayMonitors) ) ).($group?' ('.$group['Name'].')':''), canView( 'Groups' ) ); ?></li> <li><a href="?view=groups"<?php echo $view=='groups'?' class="selected"':''?>><?php echo translate('Groups') ?></a></li>
<li><a href="?view=filter">Filters</a></li> <li><a href="?view=filter"<?php echo $view=='filter'?' class="selected"':''?>><?php echo translate('Filters') ?></a></li>
<?php <?php
$cycleGroup = isset($_COOKIE['zmGroup'])?$_COOKIE['zmGroup']:0; if ( canView( 'Stream' ) ) {
if ( canView( 'Stream' ) && $cycleCount > 1 ) {
?> ?>
<li><?php echo makePopupLink( '?view=cycle&amp;group='.$cycleGroup, 'zmCycle'.$cycleGroup, array( 'cycle', $cycleWidth, $cycleHeight ), translate('Cycle'), $running ) ?></li> <li><a href="?view=cycle"<?php echo $view=='cycle'?' class="selected"':''?>><?php echo translate('Cycle') ?></a></li>
<li><?php echo makePopupLink( '?view=montage&amp;group='.$cycleGroup, 'zmMontage'.$cycleGroup, 'montage', translate('Montage'), $running ) ?></li> <li><a href="?view=montage"<?php echo $view=='montage'?' class="selected"':''?>><?php echo translate('Montage') ?></a></li>
<?php <?php
} }
if ( canView('Events') ) { if ( canView('Events') ) {
?> ?>
<li><?php echo makePopupLink( '?view=montagereview&amp;group='.$cycleGroup, 'zmMontageReview'.$cycleGroup, 'montagereview', translate('MontageReview') ) ?></li> <li><a href="?view=montagereview"<?php echo $view=='montagereview'?' class="selected"':''?>><?php echo translate('MontageReview')?></a></li>
<?php <?php
} }
?> ?>
@ -248,7 +229,7 @@ function getNavBarHTML() {
</div> <!-- End .container-fluid --> </div> <!-- End .container-fluid -->
<div class="container-fluid"> <div class="container-fluid">
<div class="pull-left"> <div class="pull-left">
<?php echo makePopupLink( '?view=bandwidth', 'zmBandwidth', 'bandwidth', $bwArray[$_COOKIE['zmBandwidth']], ($user && $user['MaxBandwidth'] != 'low' ) ) ?> <?php echo translate('BandwidthHead') ?> <?php echo makePopupLink( '?view=bandwidth', 'zmBandwidth', 'bandwidth', $bwArray[$_COOKIE['zmBandwidth']] . ' ' . translate('BandwidthHead'), ($user && $user['MaxBandwidth'] != 'low' ) ) ?>
</div> </div>
<div class="pull-right"> <div class="pull-right">
<?php echo makePopupLink( '?view=version', 'zmVersion', 'version', '<span class="'.$versionClass.'">v'.ZM_VERSION.'</span>', canEdit( 'System' ) ) ?> <?php echo makePopupLink( '?view=version', 'zmVersion', 'version', '<span class="'.$versionClass.'">v'.ZM_VERSION.'</span>', canEdit( 'System' ) ) ?>

View File

@ -45,6 +45,10 @@ function newWindow( url, name, width, height ) {
} }
function getPopupSize( tag, width, height ) { function getPopupSize( tag, width, height ) {
if ( typeof popupSizes == 'undefined' ) {
Error( "Can't find any window sizes" );
return( { 'width': 0, 'height': 0 } );
}
var popupSize = Object.clone( popupSizes[tag] ); var popupSize = Object.clone( popupSizes[tag] );
if ( !popupSize ) { if ( !popupSize ) {
Error( "Can't find window size for tag '"+tag+"'" ); Error( "Can't find window size for tag '"+tag+"'" );
@ -83,7 +87,6 @@ function getPopupSize( tag, width, height ) {
Warning( "Adjusting to minimum height ("+popupSize.minHeight+") when getting popup size for tag '"+tag+"' because calculated height is " + popupSize.height ); Warning( "Adjusting to minimum height ("+popupSize.minHeight+") when getting popup size for tag '"+tag+"' because calculated height is " + popupSize.height );
popupSize.height = popupSize.minHeight; popupSize.height = popupSize.minHeight;
} }
Debug( popupSize );
return( popupSize ); return( popupSize );
} }
@ -226,6 +229,14 @@ function submitTab( tab ) {
form.submit(); form.submit();
} }
function toggleCheckbox( element, name ) {
var form = element.form;
var checked = element.checked;
for (var i = 0; i < form.elements.length; i++)
if (form.elements[i].name.indexOf(name) == 0)
form.elements[i].checked = checked;
}
function configureDeleteButton( element ) { function configureDeleteButton( element ) {
var form = element.form; var form = element.form;
var checked = element.checked; var checked = element.checked;
@ -293,3 +304,14 @@ function addVideoTimingTrack(video, LabelFormat, monitorName, duration, startTim
track.src = 'data:plain/text;charset=utf-8,'+encodeURIComponent(webvttdata); track.src = 'data:plain/text;charset=utf-8,'+encodeURIComponent(webvttdata);
video.appendChild(track); video.appendChild(track);
} }
function changeGroup( e, depth ) {
var group_id = $('group'+depth).get('value');
Cookie.write( 'zmGroup'+depth, group_id, { duration: 10*365 } );
window.location = window.location;
}
function changeMonitor( e ) {
var monitor_id = e.value;
Cookie.write( 'zmMonitorId', monitor_id, { duration: 10*365 } );
window.location = window.location;
}

View File

@ -61,6 +61,7 @@ var focusWindow = <?php echo !empty($focusWindow)?'true':'false' ?>;
var imagePrefix = "<?php echo viewImagePath( "", '&' ) ?>"; var imagePrefix = "<?php echo viewImagePath( "", '&' ) ?>";
var auth_hash;
<?php if ( ZM_OPT_USE_AUTH && ZM_AUTH_HASH_LOGINS ) { ?> <?php if ( ZM_OPT_USE_AUTH && ZM_AUTH_HASH_LOGINS ) { ?>
var auth_hash = '<?php echo isset($_SESSION['AuthHash']) ? $_SESSION['AuthHash'] : ''; ?>'; auth_hash = '<?php echo isset($_SESSION['AuthHash']) ? $_SESSION['AuthHash'] : ''; ?>';
<?php } ?> <?php } ?>

View File

@ -19,8 +19,12 @@
// //
$servers = Server::find_all(); $servers = Server::find_all();
require_once('includes/Storage.php');
$storage_areas = Storage::find_all(); $storage_areas = Storage::find_all();
$StorageById = array();
foreach ( $storage_areas as $S ) {
$StorageById[$S->Id()] = $S;
}
$show_storage_areas = count($storage_areas) > 1 and canEdit( 'System' ) ? 1 : 0; $show_storage_areas = count($storage_areas) > 1 and canEdit( 'System' ) ? 1 : 0;
if ( $running == null ) if ( $running == null )
$running = daemonCheck(); $running = daemonCheck();
@ -92,33 +96,8 @@ $eventCounts = array(
), ),
); );
$displayMonitors = NULL;
# Also populates displayMonitors
$navbar = getNavBarHTML(); $navbar = getNavBarHTML();
$zoneCount = 0;
for( $i = 0; $i < count($displayMonitors); $i += 1 ) {
$monitor = $displayMonitors[$i];
$monitor['zmc'] = zmcStatus( $monitor );
$monitor['zma'] = zmaStatus( $monitor );
$monitor['ZoneCount'] = dbFetchOne( 'select count(Id) as ZoneCount from Zones where MonitorId = ?', 'ZoneCount', array($monitor['Id']) );
$counts = array();
for ( $j = 0; $j < count($eventCounts); $j += 1 ) {
$filter = addFilterTerm( $eventCounts[$j]['filter'], count($eventCounts[$j]['filter']['Query']['terms']), array( 'cnj' => 'and', 'attr' => 'MonitorId', 'op' => '=', 'val' => $monitor['Id'] ) );
parseFilter( $filter );
$counts[] = 'count(if(1'.$filter['sql'].",1,NULL)) as EventCount$j";
$monitor['eventCounts'][$j]['filter'] = $filter;
}
$sql = 'select '.join($counts,', ').' from Events as E where MonitorId = ?';
$counts = dbFetchOne( $sql, NULL, array($monitor['Id']) );
if ( $counts )
$displayMonitors[$i] = $monitor = array_merge( $monitor, $counts );
for ( $j = 0; $j < count($eventCounts); $j += 1 ) {
$eventCounts[$j]['total'] += $monitor['EventCount'.$j];
}
$zoneCount += $monitor['ZoneCount'];
}
noCacheHeaders(); noCacheHeaders();
@ -137,6 +116,79 @@ xhtmlHeaders( __FILE__, translate('Console') );
<input type="hidden" name="action" value=""/> <input type="hidden" name="action" value=""/>
<?php echo $navbar ?> <?php echo $navbar ?>
<div class="controlHeader">
<span id="groupControl"><label><?php echo translate('Group') ?>:</label>
<?php
# This will end up with the group_id of the deepest selection
$group_id = Group::get_group_dropdowns();
$groupSql = Group::get_group_sql( $group_id );
?>
</span>
<span id="monitorControl"><label><?php echo translate('Monitor') ?>:</label>
<?php
$monitor_id = 0;
if ( isset( $_REQUEST['monitor_id'] ) ) {
$monitor_id = $_REQUEST['monitor_id'];
} else if ( isset($_COOKIE['zmMonitorId']) ) {
$monitor_id = $_COOKIE['zmMonitorId'];
}
$maxWidth = 0;
$maxHeight = 0;
# Used to determine if the Cycle button should be made available
$monitors = dbFetchAll( 'SELECT * FROM Monitors'.($groupSql?' WHERE '.$groupSql:'').' ORDER BY Sequence ASC' );
$displayMonitors = array();
$monitors_dropdown = array(''=>'All');
for ( $i = 0; $i < count($monitors); $i++ ) {
if ( $monitor_id and ( $monitors[$i]['Id'] != $monitor_id ) ) {
continue;
}
if ( !visibleMonitor( $monitors[$i]['Id'] ) ) {
continue;
}
if ( $monitors[$i]['Function'] != 'None' ) {
$scaleWidth = reScale( $monitors[$i]['Width'], $monitors[$i]['DefaultScale'], ZM_WEB_DEFAULT_SCALE );
$scaleHeight = reScale( $monitors[$i]['Height'], $monitors[$i]['DefaultScale'], ZM_WEB_DEFAULT_SCALE );
if ( $maxWidth < $scaleWidth ) $maxWidth = $scaleWidth;
if ( $maxHeight < $scaleHeight ) $maxHeight = $scaleHeight;
}
$displayMonitors[] = $monitors[$i];
$monitors_dropdown[$monitors[$i]['Id']] = $monitors[$i]['Name'];
}
echo htmlSelect( 'monitor_id', $monitors_dropdown, $monitor_id, array('onchange'=>'changeMonitor(this);') );
$cycleWidth = $maxWidth;
$cycleHeight = $maxHeight;
$zoneCount = 0;
for( $i = 0; $i < count($displayMonitors); $i += 1 ) {
$monitor = $displayMonitors[$i];
$monitor['zmc'] = zmcStatus( $monitor );
$monitor['zma'] = zmaStatus( $monitor );
$monitor['ZoneCount'] = dbFetchOne( 'select count(Id) as ZoneCount from Zones where MonitorId = ?', 'ZoneCount', array($monitor['Id']) );
$counts = array();
for ( $j = 0; $j < count($eventCounts); $j += 1 ) {
$filter = addFilterTerm( $eventCounts[$j]['filter'], count($eventCounts[$j]['filter']['Query']['terms']), array( 'cnj' => 'and', 'attr' => 'MonitorId', 'op' => '=', 'val' => $monitor['Id'] ) );
parseFilter( $filter );
$counts[] = 'count(if(1'.$filter['sql'].",1,NULL)) as EventCount$j";
$monitor['eventCounts'][$j]['filter'] = $filter;
}
$sql = 'SELECt '.join($counts,', ').' from Events as E where MonitorId = ?';
$counts = dbFetchOne( $sql, NULL, array($monitor['Id']) );
if ( $counts )
$displayMonitors[$i] = $monitor = array_merge( $monitor, $counts );
for ( $j = 0; $j < count($eventCounts); $j += 1 ) {
$eventCounts[$j]['total'] += $monitor['EventCount'.$j];
}
$zoneCount += $monitor['ZoneCount'];
}
?>
</span>
</div>
<div class="container-fluid"> <div class="container-fluid">
<table class="table table-striped table-hover table-condensed" id="consoleTable"> <table class="table table-striped table-hover table-condensed" id="consoleTable">
@ -159,7 +211,7 @@ xhtmlHeaders( __FILE__, translate('Console') );
<?php } ?> <?php } ?>
<th class="colZones"><a href="<?php echo $_SERVER['PHP_SELF'] ?>?view=zones_overview"><?php echo translate('Zones') ?></a></th> <th class="colZones"><a href="<?php echo $_SERVER['PHP_SELF'] ?>?view=zones_overview"><?php echo translate('Zones') ?></a></th>
<?php if ( canEdit('Monitors') ) { ?> <?php if ( canEdit('Monitors') ) { ?>
<th class="colMark"><?php echo translate('Mark') ?></th> <th class="colMark"><input type="checkbox" name="toggleCheck" value="1" onclick="toggleCheckbox( this, 'markMids[]' );"<?php if ( !canEdit( 'Monitors' ) ) { ?> disabled="disabled"<?php } ?>/> <?php echo translate('All') ?></th>
<?php } ?> <?php } ?>
</tr> </tr>
</thead> </thead>
@ -220,7 +272,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
echo '<td class="colSource">'. makePopupLink( '?view=monitor&amp;mid='.$monitor['Id'], 'zmMonitor'.$monitor['Id'], 'monitor', '<span class="'.$dclass.'">'.$source.'</span>', canEdit( 'Monitors' ) ).'</td>'; echo '<td class="colSource">'. makePopupLink( '?view=monitor&amp;mid='.$monitor['Id'], 'zmMonitor'.$monitor['Id'], 'monitor', '<span class="'.$dclass.'">'.$source.'</span>', canEdit( 'Monitors' ) ).'</td>';
if ( $show_storage_areas ) { if ( $show_storage_areas ) {
?> ?>
<td class="colStorage"><?php $Storage = new Storage( $monitor['StorageId'] ); echo $Storage->Name(); ?></td> <td class="colStorage"><?php if ( isset( $StorageById[ $monitor['StorageId'] ] ) ) { echo $StorageById[ $monitor['StorageId'] ]->Name(); } ?></td>
<?php <?php
} }
@ -249,8 +301,8 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
<tfoot> <tfoot>
<tr> <tr>
<td class="colLeftButtons" colspan="<?php echo $left_columns ?>"> <td class="colLeftButtons" colspan="<?php echo $left_columns ?>">
<input type="button" class="btn btn-primary" value="<?php echo translate('Refresh') ?>" onclick="location.reload(true);"/> <input type="button" value="<?php echo translate('Refresh') ?>" onclick="location.reload(true);"/>
<input type="button" class="btn btn-primary" name="addBtn" value="<?php echo translate('AddNewMonitor') ?>" onclick="addMonitor( this )"/> <input type="button" name="addBtn" value="<?php echo translate('AddNewMonitor') ?>" onclick="addMonitor( this )"/>
<!-- <?php echo makePopupButton( '?view=monitor', 'zmMonitor0', 'monitor', translate('AddNewMonitor'), (canEdit( 'Monitors' ) && !$user['MonitorIds']) ) ?> --> <!-- <?php echo makePopupButton( '?view=monitor', 'zmMonitor0', 'monitor', translate('AddNewMonitor'), (canEdit( 'Monitors' ) && !$user['MonitorIds']) ) ?> -->
<?php echo makePopupButton( '?view=filter&amp;filter[terms][0][attr]=DateTime&amp;filter[terms][0][op]=%3c&amp;filter[terms][0][val]=now', 'zmFilter', 'filter', translate('Filters'), canView( 'Events' ) ) ?> <?php echo makePopupButton( '?view=filter&amp;filter[terms][0][attr]=DateTime&amp;filter[terms][0][op]=%3c&amp;filter[terms][0][val]=now', 'zmFilter', 'filter', translate('Filters'), canView( 'Events' ) ) ?>
<input type="button" name="editBtn" value="<?php echo translate('Edit') ?>" onclick="editMonitor( this )" disabled="disabled"/> <input type="button" name="editBtn" value="<?php echo translate('Edit') ?>" onclick="editMonitor( this )" disabled="disabled"/>

View File

@ -32,14 +32,39 @@ if ( empty($_REQUEST['mode']) ) {
$mode = validHtmlStr($_REQUEST['mode']); $mode = validHtmlStr($_REQUEST['mode']);
} }
$group = ''; $group_id = 0;
$groupSql = ''; if ( isset($_REQUEST['group']) ) {
if ( !empty($_REQUEST['group']) ) { $group_id = $_REQUEST['group'];
$group = validInt($_REQUEST['group']); } else if ( isset($_COOKIE['zmGroup'] ) ) {
$row = dbFetchOne( 'SELECT * FROM Groups WHERE Id = ?', NULL, array($group) ); $group_id = $_COOKIE['zmGroup'];
$groupSql = " and find_in_set( Id, '".$row['MonitorIds']."' )";
} }
$subgroup_id = 0;
if ( isset($_REQUEST['subgroup']) ) {
$subgroup_id = $_REQUEST['subgroup'];
} else if ( isset($_COOKIE['zmSubGroup'] ) ) {
$subgroup_id = $_COOKIE['zmSubGroup'];
}
$groupIds = null;
if ( $group_id ) {
$groupIds = array();
if ( $group = dbFetchOne( 'SELECT MonitorIds FROM Groups WHERE Id = ?', NULL, array($group_id) ) )
if ( $group['MonitorIds'] )
$groupIds = explode( ',', $group['MonitorIds'] );
if ( $subgroup_id ) {
if ( $group = dbFetchOne( 'SELECT MonitorIds FROM Groups WHERE Id = ?', NULL, array($subgroup_id) ) )
if ( $group['MonitorIds'] )
$groupIds = array_merge( $groupIds, explode( ',', $group['MonitorIds'] ) );
} else {
foreach ( dbFetchAll( 'SELECT MonitorIds FROM Groups WHERE ParentId = ?', NULL, array($group_id) ) as $group )
if ( $group['MonitorIds'] )
$groupIds = array_merge( $groupIds, explode( ',', $group['MonitorIds'] ) );
}
}
$groupSql = '';
if ( $groupIds )
$groupSql = " and find_in_set( Id, '".implode( ',', $groupIds )."' )";
$sql = "SELECT * FROM Monitors WHERE Function != 'None'$groupSql ORDER BY Sequence"; $sql = "SELECT * FROM Monitors WHERE Function != 'None'$groupSql ORDER BY Sequence";
$monitors = array(); $monitors = array();
$monIdx = 0; $monIdx = 0;
@ -53,38 +78,59 @@ foreach( dbFetchAll( $sql ) as $row ) {
$monitors[] = new Monitor( $row ); $monitors[] = new Monitor( $row );
} }
$monitor = $monitors[$monIdx]; if ( $monitors ) {
$nextMid = $monIdx==(count($monitors)-1)?$monitors[0]->Id():$monitors[$monIdx+1]->Id(); $monitor = $monitors[$monIdx];
$montageWidth = $monitor->ScaledWidth(); $nextMid = $monIdx==(count($monitors)-1)?$monitors[0]->Id():$monitors[$monIdx+1]->Id();
$montageHeight = $monitor->ScaledHeight(); $montageWidth = $monitor->ScaledWidth();
$widthScale = ($montageWidth*SCALE_BASE)/$monitor->Width(); $montageHeight = $monitor->ScaledHeight();
$heightScale = ($montageHeight*SCALE_BASE)/$monitor->Height(); $widthScale = ($montageWidth*SCALE_BASE)/$monitor->Width();
$scale = (int)(($widthScale<$heightScale)?$widthScale:$heightScale); $heightScale = ($montageHeight*SCALE_BASE)/$monitor->Height();
$scale = (int)(($widthScale<$heightScale)?$widthScale:$heightScale);
}
noCacheHeaders(); noCacheHeaders();
$focusWindow = true;
xhtmlHeaders(__FILE__, translate('CycleWatch') ); xhtmlHeaders(__FILE__, translate('CycleWatch') );
?> ?>
<body> <body>
<div id="page"> <div id="page">
<?php echo $navbar = getNavBarHTML(); ?>
<div id="header"> <div id="header">
<div id="headerButtons"> <div id="headerButtons">
<?php if ( $mode == "stream" ) { ?> <?php if ( $mode == "stream" ) { ?>
<a href="?view=<?php echo $view ?>&amp;mode=still&amp;group=<?php echo $group ?>&amp;mid=<?php echo $monitor->Id() ?>"><?php echo translate('Stills') ?></a> <a href="?view=<?php echo $view ?>&amp;mode=still&amp;mid=<?php echo $monitor ? $monitor->Id() : '' ?>"><?php echo translate('Stills') ?></a>
<?php } else { ?> <?php } else { ?>
<a href="?view=<?php echo $view ?>&amp;mode=stream&amp;group=<?php echo $group ?>&amp;mid=<?php echo $monitor->Id() ?>"><?php echo translate('Stream') ?></a> <a href="?view=<?php echo $view ?>&amp;mode=stream&amp;mid=<?php echo $monitor ? $monitor->Id() : '' ?>"><?php echo translate('Stream') ?></a>
<?php } ?> <?php } ?>
<a href="#" onclick="closeWindow(); return( false );"><?php echo translate('Close') ?></a>
</div> </div>
<h2><?php echo translate('Cycle') ?> - <?php echo validHtmlStr($monitor->Name()) ?></h2> <div class="controlHeader">
<span id="groupControl"><label><?php echo translate('Group') ?>:</label>
<?php
$groups = array(0=>'All');
foreach ( Group::find_all( array('ParentId'=>null) ) as $Group ) {
$groups[$Group->Id()] = $Group->Name();
}
echo htmlSelect( 'group', $groups, $group_id, 'changeGroup(this);' );
$groups = array(0=>'All');
if ( $group_id ) {
foreach ( Group::find_all( array('ParentId'=>$group_id) ) as $Group ) {
$groups[$Group->Id()] = $Group->Name();
}
}
echo htmlSelect( 'subgroup', $groups, $subgroup_id, 'changeSubGroup(this);' );
?>
</div>
</div> </div>
<div id="content"> <div id="content">
<div id="imageFeed"> <div id="imageFeed">
<?php echo getStreamHTML( $monitor, array( 'scale'=>$scale, 'mode'=>$mode ) ); ?> <?php
if ( $monitor ) {
echo getStreamHTML( $monitor, array( 'scale'=>$scale, 'mode'=>$mode ) );
} else {
echo "There are no monitors to view.";
}
?>
</div> </div>
</div> </div>
</div> </div>
</body> <?php xhtmlFooter() ?>
</html>

View File

@ -104,7 +104,7 @@ if ( ! $Event->Id() ) {
} else { } else {
?> ?>
<div id="dataBar"> <div id="dataBar">
<table id="dataTable" class="major" cellspacing="0"> <table id="dataTable" class="major">
<tr> <tr>
<td><span id="dataId" title="<?php echo translate('Id') ?>"><?php echo $Event->Id() ?></span></td> <td><span id="dataId" title="<?php echo translate('Id') ?>"><?php echo $Event->Id() ?></span></td>
<td><span id="dataCause" title="<?php echo $Event->Notes()?validHtmlStr($Event->Notes()):translate('AttrCause') ?>"><?php echo validHtmlStr($Event->Cause()) ?></span></td> <td><span id="dataCause" title="<?php echo $Event->Notes()?validHtmlStr($Event->Notes()):translate('AttrCause') ?>"><?php echo validHtmlStr($Event->Cause()) ?></span></td>
@ -126,16 +126,16 @@ if ( ! $Event->Id() ) {
<div id="menuBar2"> <div id="menuBar2">
<div id="closeWindow"><a href="#" onclick="closeWindow();"><?php echo translate('Close') ?></a></div> <div id="closeWindow"><a href="#" onclick="closeWindow();"><?php echo translate('Close') ?></a></div>
<?php <?php
if ( canEdit( 'Events' ) ) { if ( canEdit('Events') ) {
?> ?>
<div id="deleteEvent"><a href="#" onclick="deleteEvent()"><?php echo translate('Delete') ?></a></div> <div id="deleteEvent"><a href="#" onclick="deleteEvent()"><?php echo translate('Delete') ?></a></div>
<div id="editEvent"><a href="#" onclick="editEvent()"><?php echo translate('Edit') ?></a></div> <div id="editEvent"><a href="#" onclick="editEvent()"><?php echo translate('Edit') ?></a></div>
<div id="archiveEvent" class="hidden"><a href="#" onclick="archiveEvent()"><?php echo translate('Archive') ?></a></div> <div id="archiveEvent"<?php echo $Event->Archived == 1 ? ' class="hidden"' : '' ?>><a href="#" onclick="archiveEvent()"><?php echo translate('Archive') ?></a></div>
<div id="unarchiveEvent" class="hidden"><a href="#" onclick="unarchiveEvent()"><?php echo translate('Unarchive') ?></a></div> <div id="unarchiveEvent"<?php echo $Event->Archived == 0 ? ' class="hidden"' : '' ?>><a href="#" onclick="unarchiveEvent()"><?php echo translate('Unarchive') ?></a></div>
<?php <?php
} // end if can edit Events } // end if can edit Events
if ( $Event->DefaultVideo() ) { ?> if ( $Event->DefaultVideo() ) { ?>
<div id="downloadEventFile"><a href="<?php echo $Event->getStreamSrc()?>">Download MP4</a></div> <div id="downloadEventFile"><a href="<?php echo $Event->getStreamSrc(array('mode'=>'mp4'))?>">Download MP4</a></div>
<?php <?php
} // end if Event->DefaultVideo } // end if Event->DefaultVideo
?> ?>
@ -167,12 +167,23 @@ if ( $Event->DefaultVideo() ) {
var duration = <?php echo $Event->Length() ?>, startTime = '<?php echo $Event->StartTime() ?>'; var duration = <?php echo $Event->Length() ?>, startTime = '<?php echo $Event->StartTime() ?>';
addVideoTimingTrack(document.getElementById('videoobj'), LabelFormat, monitorName, duration, startTime); addVideoTimingTrack(document.getElementById('videoobj'), LabelFormat, monitorName, duration, startTime);
nearEventsQuery( eventData.Id );
vjsReplay(<?php echo (strtotime($Event->StartTime()) + $Event->Length())*1000 ?>);
</script> </script>
<p id="replayAllCountDown"></p>
<p id="dvrControlsVjs" class="dvrControls">
<input type="button" value="&lt;+" id="prevBtnVjs" title="<?php echo translate('Prev') ?>" class="active" onclick="streamPrev( true );"/>
<input type="button" value="+&gt;" id="nextBtnVjs" title="<?php echo translate('Next') ?>" class="active" onclick="streamNext( true );"/>
</p>
<?php <?php
} // end if DefaultVideo } // end if DefaultVideo
?> ?>
</div><!--eventVideo--> </div><!--eventVideo-->
<div id="imageFeed"<?php if ( $Event->DefaultVideo() ) { ?> class="hidden"<?php } ?>> <?php if (!$Event->DefaultVideo()) { ?>
<div id="imageFeed">
<?php <?php
if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) { if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
$streamSrc = $Event->getStreamSrc( array( 'mode'=>'mpeg', 'scale'=>$scale, 'rate'=>$rate, 'bitrate'=>ZM_WEB_VIDEO_BITRATE, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'format'=>ZM_MPEG_REPLAY_FORMAT, 'replay'=>$replayMode ) ); $streamSrc = $Event->getStreamSrc( array( 'mode'=>'mpeg', 'scale'=>$scale, 'rate'=>$rate, 'bitrate'=>ZM_WEB_VIDEO_BITRATE, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'format'=>ZM_MPEG_REPLAY_FORMAT, 'replay'=>$replayMode ) );
@ -186,7 +197,7 @@ if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
} }
} // end if stream method } // end if stream method
?> ?>
<p id="dvrControls"> <p id="dvrControls" class="dvrControls">
<input type="button" value="&lt;+" id="prevBtn" title="<?php echo translate('Prev') ?>" class="inactive" onclick="streamPrev( true );"/> <input type="button" value="&lt;+" id="prevBtn" title="<?php echo translate('Prev') ?>" class="inactive" onclick="streamPrev( true );"/>
<input type="button" value="&lt;&lt;" id="fastRevBtn" title="<?php echo translate('Rewind') ?>" class="inactive" disabled="disabled" onclick="streamFastRev( true );"/> <input type="button" value="&lt;&lt;" id="fastRevBtn" title="<?php echo translate('Rewind') ?>" class="inactive" disabled="disabled" onclick="streamFastRev( true );"/>
<input type="button" value="&lt;" id="slowRevBtn" title="<?php echo translate('StepBack') ?>" class="unavail" disabled="disabled" onclick="streamSlowRev( true );"/> <input type="button" value="&lt;" id="slowRevBtn" title="<?php echo translate('StepBack') ?>" class="unavail" disabled="disabled" onclick="streamSlowRev( true );"/>
@ -210,6 +221,7 @@ if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
</div><!--progressBar--> </div><!--progressBar-->
</div><!--imageFeed--> </div><!--imageFeed-->
</div> </div>
<?php } /*end if !DefaultVideo*/ ?>
<?php <?php
if ( $Event->SaveJPEGs() & 3 ) { // frames or analysis if ( $Event->SaveJPEGs() & 3 ) { // frames or analysis
?> ?>
@ -220,7 +232,7 @@ if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
</div> </div>
<div id="eventImagePanel"> <div id="eventImagePanel">
<div id="eventImageFrame"> <div id="eventImageFrame">
<img id="eventImage" src="graphics/transparent.gif" alt=""/> <img id="eventImage" src="graphics/transparent.png" alt=""/>
<div id="eventImageBar"> <div id="eventImageBar">
<div id="eventImageClose"><input type="button" value="<?php echo translate('Close') ?>" onclick="hideEventImage()"/></div> <div id="eventImageClose"><input type="button" value="<?php echo translate('Close') ?>" onclick="hideEventImage()"/></div>
<div id="eventImageStats" class="hidden"><input type="button" value="<?php echo translate('Stats') ?>" onclick="showFrameStats()"/></div> <div id="eventImageStats" class="hidden"><input type="button" value="<?php echo translate('Stats') ?>" onclick="showFrameStats()"/></div>

View File

@ -18,17 +18,12 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// //
if ( !canView( 'Events' ) ) if ( !canView( 'Events' ) ) {
{ $view = 'error';
$view = "error";
return; return;
} }
# Must re-start session because we close it now in index.php to improve concurrency if ( isset($_SESSION['export']) ) {
session_start();
if ( isset($_SESSION['export']) )
{
if ( isset($_SESSION['export']['detail']) ) if ( isset($_SESSION['export']['detail']) )
$_REQUEST['exportDetail'] = $_SESSION['export']['detail']; $_REQUEST['exportDetail'] = $_SESSION['export']['detail'];
if ( isset($_SESSION['export']['frames']) ) if ( isset($_SESSION['export']['frames']) )

View File

@ -18,56 +18,98 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// //
if ( !canEdit( 'Groups' ) ) if ( !canEdit( 'Groups' ) ) {
{ $view = 'error';
$view = "error";
return; return;
} }
if ( !empty($_REQUEST['gid']) ) if ( !empty($_REQUEST['gid']) ) {
{ $newGroup = new Group( $_REQUEST['gid'] );
$newGroup = dbFetchGroup( $_REQUEST['gid'] ); } else {
} $newGroup = new Group();
else
{
$newGroup = array(
"Id" => "",
"Name" => "New Group",
"MonitorIds" => ""
);
} }
xhtmlHeaders( __FILE__, translate('Group')." - ".$newGroup['Name'] ); xhtmlHeaders( __FILE__, translate('Group').' - '.$newGroup->Name() );
?> ?>
<body> <body>
<div id="page"> <div id="page">
<div id="header"> <div id="header">
<h2><?php echo translate('Group') ?> - <?php echo $newGroup['Name'] ?></h2> <h2><?php echo translate('Group') ?> - <?php echo $newGroup->Name() ?></h2>
</div> </div>
<div id="content"> <div id="content">
<form name="groupForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>"> <form name="groupForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>">
<input type="hidden" name="view" value="<?php echo $view ?>"/> <input type="hidden" name="view" value="<?php echo $view ?>"/>
<input type="hidden" name="action" value="group"/> <input type="hidden" name="action" value="group"/>
<input type="hidden" name="gid" value="<?php echo $newGroup['Id'] ?>"/> <input type="hidden" name="gid" value="<?php echo $newGroup->Id() ?>"/>
<table id="contentTable" class="major" cellspacing="0"> <table id="contentTable" class="major">
<tbody> <tbody>
<tr> <tr>
<th scope="row"><?php echo translate('Name') ?></th> <th scope="row"><?php echo translate('Name') ?></th>
<td><input type="text" name="newGroup[Name]" value="<?php echo validHtmlStr($newGroup['Name']) ?>"/></td> <td><input type="text" name="newGroup[Name]" value="<?php echo validHtmlStr($newGroup->Name()) ?>" oninput="configureButtons(this);"/></td>
</tr> </tr>
<tr> <tr>
<th scope="row"><?php echo translate('MonitorIds') ?></th> <th scope="row"><?php echo translate('ParentGroup') ?></th>
<td> <td>
<select name="newGroup[MonitorIds][]" size="4" multiple="multiple">
<?php <?php
$monitors = dbFetchAll( "select Id,Name from Monitors order by Sequence asc" ); $Groups = array();
$monitorIds = array_flip( explode( ',', $newGroup['MonitorIds'] ) ); foreach ( Group::find_all( ) as $Group ) {
foreach ( $monitors as $monitor ) $Groups[$Group->Id()] = $Group;
{ }
if ( visibleMonitor( $monitor['Id'] ) )
{ # This array is indexed by parent_id
$children = array();
foreach ( $Groups as $id=>$Group ) {
if ( $Group->ParentId() != null ) {
if ( ! isset( $children[$Group->ParentId()] ) )
$children[$Group->ParentId()] = array();
$children[$Group->ParentId()][] = $Group;
}
}
function get_Id( $G ) {
return $G->Id();
}
function get_children($Group) {
global $children;
$kids = array();
if ( isset( $children[$Group->Id()] ) ) {
$kids += array_map( 'get_Id', $children[$Group->Id()] );
foreach ( $children[$Group->Id()] as $G ) {
foreach ( get_children($G) as $id ) {
$kids[] = $id;
}
}
}
return $kids;
}
$kids = get_children($newGroup);
$kids[] = $newGroup->Id();
function get_question_marks() {
return '?';
}
$options = array(''=>'None');
foreach ( dbFetchAll( 'SELECT Id,Name from Groups WHERE Id NOT IN ('.implode(',',array_map('get_question_marks', $kids )).') ORDER BY Name', null, $kids ) as $option ) {
$options[$option['Id']] = $option['Name'];
}
echo htmlSelect( 'newGroup[ParentId]', $options, $newGroup->ParentId(), array('onchange'=>'configureButtons(this);' ));
?> ?>
<option value="<?php echo $monitor['Id'] ?>"<?php if ( array_key_exists( $monitor['Id'], $monitorIds ) ) { ?> selected="selected"<?php } ?> onclick="configureButtons( this );"><?php echo validHtmlStr($monitor['Name']) ?></option> </td>
</tr>
<tr>
<th scope="row"><?php echo translate('Monitor') ?></th>
<td>
<select name="newGroup[MonitorIds][]" size="4" multiple="multiple" onchange="configureButtons(this);">
<?php
$monitors = dbFetchAll( 'SELECT Id,Name FROM Monitors ORDER BY Sequence ASC' );
$monitorIds = array_flip( explode( ',', $newGroup->MonitorIds() ) );
foreach ( $monitors as $monitor ) {
if ( visibleMonitor( $monitor['Id'] ) ) {
?>
<option value="<?php echo $monitor['Id'] ?>"<?php if ( array_key_exists( $monitor['Id'], $monitorIds ) ) { ?> selected="selected"<?php } ?>><?php echo validHtmlStr($monitor['Name']) ?></option>
<?php <?php
} }
} }
@ -78,7 +120,7 @@ xhtmlHeaders( __FILE__, translate('Group')." - ".$newGroup['Name'] );
</tbody> </tbody>
</table> </table>
<div id="contentButtons"> <div id="contentButtons">
<input type="submit" name="saveBtn" value="<?php echo translate('Save') ?>" disabled="disabled" /> <input type="submit" name="saveBtn" value="<?php echo translate('Save') ?>"<?php $newGroup->Id() ? '' : ' disabled="disabled"'?>/>
<input type="button" value="<?php echo translate('Cancel') ?>" onclick="closeWindow()"/> <input type="button" value="<?php echo translate('Cancel') ?>" onclick="closeWindow()"/>
</div> </div>
</form> </form>

View File

@ -19,67 +19,80 @@
// //
if ( !canView( 'Groups' ) ) { if ( !canView( 'Groups' ) ) {
$view = "error"; $view = 'error';
return; return;
} }
$sql = "select * from Groups order by Name"; # This will end up with the group_id of the deepest selection
$groups = array(); $group_id = 0;
$selected = false; $max_depth = 0;
foreach( dbFetchAll( $sql ) as $row )
{
if ( !empty($_COOKIE['zmGroup']) && ($row['Id'] == $_COOKIE['zmGroup']) ) $Groups = array();
{ foreach ( Group::find_all( ) as $Group ) {
$row['selected'] = true; $Groups[$Group->Id()] = $Group;
$selected = true;
}
else
{
$row['selected'] = false;
}
$groups[] = $row;
} }
# This array is indexed by parent_id
$children = array();
foreach ( $Groups as $id=>$Group ) {
if ( ! isset( $children[$Group->ParentId()] ) )
$children[$Group->ParentId()] = array();
$children[$Group->ParentId()][] = $Group;
if ( $max_depth < $Group->depth() )
$max_depth = $Group->depth();
}
Warning("Max depth $max_depth");
xhtmlHeaders(__FILE__, translate('Groups') ); xhtmlHeaders(__FILE__, translate('Groups') );
?> ?>
<body> <body>
<div id="page"> <div id="page">
<div id="header"> <?php echo $navbar = getNavBarHTML(); ?>
<h2><?php echo translate('Groups') ?></h2>
</div>
<div id="content"> <div id="content">
<form name="groupsForm" method="get" action="<?php echo $_SERVER['PHP_SELF'] ?>"> <form name="groupsForm" method="get" action="<?php echo $_SERVER['PHP_SELF'] ?>">
<input type="hidden" name="view" value="none"/> <input type="hidden" name="view" value="none"/>
<input type="hidden" name="action" value="setgroup"/> <input type="hidden" name="action" value="setgroup"/>
<table id="contentTable" class="major" cellspacing="0"> <table id="contentTable" class="major">
<thead> <thead>
<tr> <tr>
<th class="colName"><?php echo translate('Name') ?></th> <th class="colName" colspan="<?php echo $max_depth ?>"><?php echo translate('Name') ?></th>
<th class="colIds"><?php echo translate('MonitorIds') ?></th> <th class="colIds"><?php echo translate('Monitors') ?></th>
<th class="colSelect"><?php echo translate('Select') ?></th> <th class="colSelect"><?php echo translate('Mark') ?></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr class="highlight"> <?php
<td class="colName"><?php echo translate('NoGroup') ?></td> function group_line( $Group ) {
<td class="colIds"><?php echo translate('All') ?></td> global $children;
<td class="colSelect"><input type="radio" name="gid" value="0"<?php echo !$selected?' checked="checked"':'' ?> onclick="configureButtons( this );"/></td> global $max_depth;
$html = '<tr>';
for ( $i = 1; $i<$Group->depth(); $i+=1 )
$html .= '<td class="colName">&nbsp;</td>';
$html .= '<td class="colName" colspan="'.($max_depth-($Group->depth()-1)).'">';
if ( canEdit('Groups') ) {
$html .= '<a href="#" onclick="editGroup('.$Group->Id().');">'. validHtmlStr($Group->Name()).'</a>';
} else {
$html .= validHtmlStr($Group->Name());
}
$html .= '</td><td class="colIds">'. monitorIdsToNames( $Group->MonitorIds(), 30 ).'</td>
<td class="colSelect"><input type="checkbox" name="gid" value="'. $Group->Id() .'" onclick="configureButtons(this);"/></td>
</tr> </tr>
<?php foreach ( $groups as $group ) { ?> ';
<tr> if ( isset( $children[$Group->Id()] ) ) {
<td class="colName"><?php echo validHtmlStr($group['Name']) ?></td> foreach ( $children[$Group->Id()] as $G ) {
<td class="colIds"><?php echo monitorIdsToNames( $group['MonitorIds'], 30 ) ?></td> $html .= group_line( $G );
<td class="colSelect"><input type="radio" name="gid" value="<?php echo $group['Id'] ?>"<?php echo $group['selected']?' checked="checked"':'' ?> onclick="configureButtons( this );"/></td> }
</tr> }
<?php } ?> return $html;
}
foreach ( $children[null] as $Group )
echo group_line( $Group );
?>
</tbody> </tbody>
</table> </table>
<div id="contentButtons"> <div id="contentButtons">
<input type="submit" value="<?php echo translate('Apply') ?>"/> <input type="button" value="<?php echo translate('New') ?>" onclick="newGroup();"<?php echo canEdit('Groups')?'':' disabled="disabled"' ?>/>
<input type="button" value="<?php echo translate('New') ?>" onclick="newGroup()"<?php echo canEdit('Groups')?'':' disabled="disabled"' ?>/> <input type="button" name="deleteBtn" value="<?php echo translate('Delete') ?>" onclick="deleteGroup(this);" disabled="disabled"/>
<input type="button" name="editBtn" value="<?php echo translate('Edit') ?>" onclick="editGroup( this )"<?php echo $selected&&canEdit('Groups')?'':' disabled="disabled"' ?>/>
<input type="button" name="deleteBtn" value="<?php echo translate('Delete') ?>" onclick="deleteGroup( this )"<?php echo $selected&&canEdit('Groups')?'':' disabled="disabled"' ?>/>
<input type="button" value="<?php echo translate('Cancel') ?>" onclick="closeWindow();"/>
</div> </div>
</form> </form>
</div> </div>

View File

@ -1,5 +1,5 @@
function nextCycleView() { function nextCycleView() {
window.location.replace( '?view=cycle&group='+currGroup+'&mid='+nextMid+'&mode='+mode, cycleRefreshTimeout ); window.location.replace( '?view=cycle&mid='+nextMid+'&mode='+mode, cycleRefreshTimeout );
} }
function initCycle() { function initCycle() {

View File

@ -1,4 +1,3 @@
var currGroup = "<?php echo isset($_REQUEST['group'])?validJsStr($_REQUEST['group']):'' ?>";
var nextMid = "<?php echo isset($nextMid)?$nextMid:'' ?>"; var nextMid = "<?php echo isset($nextMid)?$nextMid:'' ?>";
var mode = "<?php echo $mode ?>"; var mode = "<?php echo $mode ?>";

View File

@ -1,5 +1,36 @@
var vid = null; var vid = null;
function vjsReplay(endTime) {
var video = videojs('videoobj').ready(function(){
var player = this;
player.on('ended', function() {
switch(replayMode.value) {
case 'none':
break;
case 'single':
player.play();
break;
case 'all':
// nextEventStartTime.getTime() is a mootools workaround, highjacks Date.parse
var gapDuration = (new Date().getTime()) + (nextEventStartTime.getTime() - endTime);
var x = setInterval(function() {
var now = new Date().getTime();
var remainder = new Date(Math.round(gapDuration - now)).toISOString().substr(11,8);;
$j("#replayAllCountDown").html(remainder + " to next event.");
if (remainder < 0) {
clearInterval(x);
streamNext( true );
}
}, 1000);
break;
case 'gapless':
streamNext( true );
break;
}
});
});
}
function setButtonState( element, butClass ) { function setButtonState( element, butClass ) {
if ( element ) { if ( element ) {
element.className = butClass; element.className = butClass;
@ -164,13 +195,23 @@ function streamFastRev( action ) {
} }
function streamPrev( action ) { function streamPrev( action ) {
if ( action ) if ( action ) {
if ( vid ) {
location.replace(thisUrl + '?view=event&eid=' + prevEventId + filterQuery + sortQuery);
return;
}
streamReq.send( streamParms+"&command="+CMD_PREV ); streamReq.send( streamParms+"&command="+CMD_PREV );
}
} }
function streamNext( action ) { function streamNext( action ) {
if ( action ) if ( action ) {
if ( vid ) {
location.replace(thisUrl + '?view=event&eid=' + nextEventId + filterQuery + sortQuery);
return;
}
streamReq.send( streamParms+"&command="+CMD_NEXT ); streamReq.send( streamParms+"&command="+CMD_NEXT );
}
} }
function streamZoomIn( x, y ) { function streamZoomIn( x, y ) {
@ -251,6 +292,8 @@ function eventQuery( eventId ) {
var prevEventId = 0; var prevEventId = 0;
var nextEventId = 0; var nextEventId = 0;
var prevEventStartTime = 0;
var nextEventStartTime = 0;
var PrevEventDefVideoPath = ""; var PrevEventDefVideoPath = "";
var NextEventDefVideoPath = ""; var NextEventDefVideoPath = "";
@ -259,6 +302,8 @@ function getNearEventsResponse( respObj, respText ) {
return; return;
prevEventId = respObj.nearevents.PrevEventId; prevEventId = respObj.nearevents.PrevEventId;
nextEventId = respObj.nearevents.NextEventId; nextEventId = respObj.nearevents.NextEventId;
prevEventStartTime = Date.parse(respObj.nearevents.PrevEventStartTime);
nextEventStartTime = Date.parse(respObj.nearevents.NextEventStartTime);
PrevEventDefVideoPath = respObj.nearevents.PrevEventDefVideoPath; PrevEventDefVideoPath = respObj.nearevents.PrevEventDefVideoPath;
NextEventDefVideoPath = respObj.nearevents.NextEventDefVideoPath; NextEventDefVideoPath = respObj.nearevents.NextEventDefVideoPath;
@ -266,12 +311,14 @@ function getNearEventsResponse( respObj, respText ) {
if ( prevEventBtn ) prevEventBtn.disabled = !prevEventId; if ( prevEventBtn ) prevEventBtn.disabled = !prevEventId;
var nextEventBtn = $('nextEventBtn'); var nextEventBtn = $('nextEventBtn');
if ( nextEventBtn ) nextEventBtn.disabled = !nextEventId; if ( nextEventBtn ) nextEventBtn.disabled = !nextEventId;
if (prevEventId == 0) $j('#prevBtnVjs').prop('disabled', true).attr('class', 'unavail');
if (nextEventId == 0) $j('#nextBtnVjs').prop('disabled', true).attr('class', 'unavail');
} }
var nearEventsReq = new Request.JSON( { url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getNearEventsResponse } ); var nearEventsReq = new Request.JSON( { url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getNearEventsResponse } );
function nearEventsQuery( eventId ) { function nearEventsQuery( eventId ) {
var parms = "view=request&request=status&entity=nearevents&id="+eventId; var parms = "view=request&request=status&entity=nearevents&id="+eventId+filterQuery+sortQuery;
nearEventsReq.send( parms ); nearEventsReq.send( parms );
} }
@ -455,7 +502,7 @@ function checkFrames( eventId, frameId, loadImage ) {
for ( var fid = loFid; fid <= hiFid; fid++ ) { for ( var fid = loFid; fid <= hiFid; fid++ ) {
if ( !$('eventThumb'+fid) ) { if ( !$('eventThumb'+fid) ) {
var img = new Element( 'img', { 'id': 'eventThumb'+fid, 'src': 'graphics/transparent.gif', 'alt': fid, 'class': 'placeholder' } ); var img = new Element( 'img', { 'id': 'eventThumb'+fid, 'src': 'graphics/transparent.png', 'alt': fid, 'class': 'placeholder' } );
img.addEvent( 'click', function() { eventData['frames'][fid] = null; checkFrames( eventId, fid ); } ); img.addEvent( 'click', function() { eventData['frames'][fid] = null; checkFrames( eventId, fid ); } );
frameQuery( eventId, fid, loadImage && (fid == frameId) ); frameQuery( eventId, fid, loadImage && (fid == frameId) );
var imgs = $('eventThumbs').getElements( 'img' ); var imgs = $('eventThumbs').getElements( 'img' );

View File

@ -33,8 +33,8 @@ var eventData = {
Length: '<?php echo $Event->Length() ?>' Length: '<?php echo $Event->Length() ?>'
}; };
var filterQuery = '<?php echo isset($filterQuery)?validJsStr($filterQuery):'' ?>'; var filterQuery = '<?php echo isset($filterQuery)?validJsStr(htmlspecialchars_decode($filterQuery)):'' ?>';
var sortQuery = '<?php echo isset($sortQuery)?validJsStr($sortQuery):'' ?>'; var sortQuery = '<?php echo isset($sortQuery)?validJsStr(htmlspecialchars_decode($sortQuery)):'' ?>';
var scale = <?php echo $scale ?>; var scale = <?php echo $scale ?>;
var canEditEvents = <?php echo canEdit( 'Events' )?'true':'false' ?>; var canEditEvents = <?php echo canEdit( 'Events' )?'true':'false' ?>;

View File

@ -9,7 +9,12 @@ if ( refreshParent ) {
function configureButtons( element ) { function configureButtons( element ) {
if ( canEditGroups ) { if ( canEditGroups ) {
var form = element.form; var form = element.form;
form.saveBtn.disabled = (element.value == 0); var disabled = false;
if ( form.elements['newGroup[Name]'].value == '' ) {
disabled = true;
}
form.saveBtn.disabled = disabled;
} }
} }

View File

@ -2,20 +2,14 @@ function newGroup() {
createPopup( '?view=group', 'zmGroup', 'group' ); createPopup( '?view=group', 'zmGroup', 'group' );
} }
function editGroup( element ) { function setGroup( element ) {
var form = element.form; var form = element.form;
form.action.value = 'setgroup'; form.action.value = 'setgroup';
form.submit(); form.submit();
} }
function editGroup( element ) { function editGroup( gid ) {
var form = element.form; createPopup( '?view=group&gid='+gid, 'zmGroup', 'group' );
for ( var i = 0; i < form.gid.length; i++ ) {
if ( form.gid[i].checked ) {
createPopup( '?view=group&gid='+form.gid[i].value, 'zmGroup', 'group' );
return;
}
}
} }
function deleteGroup( element ) { function deleteGroup( element ) {
@ -29,7 +23,6 @@ function configureButtons( element ) {
if ( canEditGroups ) { if ( canEditGroups ) {
var form = element.form; var form = element.form;
if ( element.checked ) { if ( element.checked ) {
form.editBtn.disabled = (element.value == 0);
form.deleteBtn.disabled = (element.value == 0); form.deleteBtn.disabled = (element.value == 0);
} }
} }

View File

@ -156,7 +156,6 @@ function changeSize() {
src = src.replace(/height=[\.\d]+/i,'height='+height ); src = src.replace(/height=[\.\d]+/i,'height='+height );
src = src.replace(/rand=\d+/i,'rand='+Math.floor((Math.random() * 1000000) )); src = src.replace(/rand=\d+/i,'rand='+Math.floor((Math.random() * 1000000) ));
streamImg.src = src; streamImg.src = src;
} }
streamImg.style.width = width? width + "px" : null; streamImg.style.width = width? width + "px" : null;
streamImg.style.height = height ? height + "px" : null; streamImg.style.height = height ? height + "px" : null;
@ -210,7 +209,6 @@ function changeScale() {
Cookie.write( 'zmMontageHeight', '', { duration: 10*365 } ); Cookie.write( 'zmMontageHeight', '', { duration: 10*365 } );
} }
var monitors = new Array(); var monitors = new Array();
function initPage() { function initPage() {
for ( var i = 0; i < monitorData.length; i++ ) { for ( var i = 0; i < monitorData.length; i++ ) {

View File

@ -166,13 +166,12 @@ function drawSliderOnGraph(val) {
// If we have data already saved first restore it from LAST time // If we have data already saved first restore it from LAST time
if(typeof underSlider !== 'undefined') if(typeof underSlider !== 'undefined') {
{
ctx.putImageData(underSlider,underSliderX, 0, 0, 0, sliderWidth, sliderHeight); ctx.putImageData(underSlider,underSliderX, 0, 0, 0, sliderWidth, sliderHeight);
underSlider=undefined; underSlider=undefined;
} }
if(liveMode==0) // we get rid of the slider if we switch to live (since it may not be in the "right" place) if ( liveMode == 0 ) {
{ // we get rid of the slider if we switch to live (since it may not be in the "right" place)
// Now save where we are putting it THIS time // Now save where we are putting it THIS time
underSlider=ctx.getImageData(sliderX, 0, sliderWidth, sliderHeight); underSlider=ctx.getImageData(sliderX, 0, sliderWidth, sliderHeight);
// And add in the slider' // And add in the slider'
@ -183,13 +182,10 @@ function drawSliderOnGraph(val) {
underSliderX=sliderX; underSliderX=sliderX;
} }
var o = $('scruboutput'); var o = $('scruboutput');
if(liveMode==1) if(liveMode==1) {
{
o.innerHTML="Live Feed @ " + (1000 / currentDisplayInterval).toFixed(1) + " fps"; o.innerHTML="Live Feed @ " + (1000 / currentDisplayInterval).toFixed(1) + " fps";
o.style.color="red"; o.style.color="red";
} } else {
else
{
o.innerHTML=secs2dbstr(val); o.innerHTML=secs2dbstr(val);
o.style.color="blue"; o.style.color="blue";
} }
@ -293,16 +289,10 @@ function drawGraph()
return; return;
} }
function redrawScreen() function redrawScreen() {
{ if ( liveMode == 1 ) {
if(fitMode==0) // if we fit, then monitors were absolutely positioned already (or will be) otherwise release them to float // if we are not in live view switch to history -- this has to come before fit in case we re-establish the timeline
{ $('DateTimeDiv').style.display="none";
for(var i=0; i<numMonitors; i++)
monitorCanvasObj[monitorPtr[i]].style.position="";
$('monitors').setStyle('height',"auto");
}
if(liveMode==1) // if we are not in live view switch to history -- this has to come before fit in case we re-establish the timeline
{
$('SpeedDiv').style.display="none"; $('SpeedDiv').style.display="none";
$('timelinediv').style.display="none"; $('timelinediv').style.display="none";
$('live').innerHTML="History"; $('live').innerHTML="History";
@ -311,9 +301,9 @@ function redrawScreen()
$('panleft').style.display="none"; $('panleft').style.display="none";
$('panright').style.display="none"; $('panright').style.display="none";
} } else {
else // switch out of liveview mode // switch out of liveview mode
{ $('DateTimeDiv').style.display="inline";
$('SpeedDiv').style.display="inline"; $('SpeedDiv').style.display="inline";
$('SpeedDiv').style.display="inline-flex"; $('SpeedDiv').style.display="inline-flex";
$('timelinediv').style.display=null; $('timelinediv').style.display=null;
@ -328,8 +318,7 @@ function redrawScreen()
$('panright').style.display="inline-flex"; $('panright').style.display="inline-flex";
} }
if(fitMode==1) if ( fitMode == 1 ) {
{
$('ScaleDiv').style.display="none"; $('ScaleDiv').style.display="none";
$('fit').innerHTML="Scale"; $('fit').innerHTML="Scale";
var vh=window.innerHeight; var vh=window.innerHeight;
@ -339,9 +328,12 @@ function redrawScreen()
$('monitors').setStyle('height',mh.toString() + "px"); // leave a small gap at bottom $('monitors').setStyle('height',mh.toString() + "px"); // leave a small gap at bottom
if(maxfit2($('monitors').getSize().x,$('monitors').getSize().y) == 0) /// if we fail to fix we back out of fit mode -- ??? This may need some better handling if(maxfit2($('monitors').getSize().x,$('monitors').getSize().y) == 0) /// if we fail to fix we back out of fit mode -- ??? This may need some better handling
fitMode=1-fitMode; fitMode=1-fitMode;
} } else {
else // switch out of fit mode // switch out of fit mode
{ // if we fit, then monitors were absolutely positioned already (or will be) otherwise release them to float
for( var i=0; i<numMonitors; i++ )
monitorCanvasObj[monitorPtr[i]].style.position="";
$('monitors').setStyle('height',"auto");
$('ScaleDiv').style.display="inline"; $('ScaleDiv').style.display="inline";
$('ScaleDiv').style.display="inline-flex"; $('ScaleDiv').style.display="inline-flex";
$('fit').innerHTML="Fit"; $('fit').innerHTML="Fit";
@ -353,11 +345,9 @@ function redrawScreen()
} }
function outputUpdate(val) function outputUpdate(val) {
{
drawSliderOnGraph(val); drawSliderOnGraph(val);
for(var i=0; i<numMonitors; i++) for(var i=0; i<numMonitors; i++) {
{
loadImage2Monitor(monitorPtr[i],SetImageSource(monitorPtr[i],val)); loadImage2Monitor(monitorPtr[i],SetImageSource(monitorPtr[i],val));
} }
var currentTimeMS = new Date(val*1000); var currentTimeMS = new Date(val*1000);
@ -402,14 +392,16 @@ function mmove(event) {
} }
} }
function secs2dbstr (s) function secs2inputstr (s) {
{ var st = (new Date(s * 1000)).format("%Y-%m-%dT%H:%M:%S");
return st;
}
function secs2dbstr (s) {
var st = (new Date(s * 1000)).format("%Y-%m-%d %H:%M:%S"); var st = (new Date(s * 1000)).format("%Y-%m-%d %H:%M:%S");
return st; return st;
} }
function setFit(value) function setFit(value) {
{
fitMode=value; fitMode=value;
redrawScreen(); redrawScreen();
} }
@ -431,13 +423,12 @@ function setScale(newscale) // makes actual change
currentScale=newscale; currentScale=newscale;
} }
function showSpeed(val) // updates slider only function showSpeed(val) {
{ // updates slider only
$('speedslideroutput').innerHTML = parseFloat(speeds[val]).toFixed(2).toString() + " x"; $('speedslideroutput').innerHTML = parseFloat(speeds[val]).toFixed(2).toString() + " x";
} }
function setSpeed(val) // Note parameter is the index not the speed function setSpeed(val) { // Note parameter is the index not the speed
{
var t; var t;
if(liveMode==1) return; // we shouldn't actually get here but just in case if(liveMode==1) return; // we shouldn't actually get here but just in case
currentSpeed=parseFloat(speeds[val]); currentSpeed=parseFloat(speeds[val]);
@ -447,8 +438,7 @@ function setSpeed(val) // Note parameter is the index not the speed
if( timerInterval != currentDisplayInterval || currentSpeed == 0 ) timerFire(); // if the timer isn't firing we need to trigger it to update if( timerInterval != currentDisplayInterval || currentSpeed == 0 ) timerFire(); // if the timer isn't firing we need to trigger it to update
} }
function setLive(value) function setLive(value) {
{
liveMode=value; liveMode=value;
redrawScreen(); redrawScreen();
} }
@ -459,19 +449,22 @@ function setLive(value)
function clicknav(minSecs,maxSecs,arch,live) {// we use the current time if we can function clicknav(minSecs,maxSecs,arch,live) {// we use the current time if we can
var now = new Date() / 1000; var now = new Date() / 1000;
var minStr=""; var minStr = "";
var maxStr=""; var maxStr = "";
var currentStr=""; var currentStr = "";
if ( minSecs > 0 ) { if ( minSecs > 0 ) {
if(maxSecs > now) if(maxSecs > now)
maxSecs = parseInt(now); maxSecs = parseInt(now);
maxStr="&maxTime=" + secs2dbstr(maxSecs); maxStr="&maxTime=" + secs2inputstr(maxSecs);
$('maxTime').value = secs2inputstr(maxSecs);
}
if ( minSecs > 0 ) {
$('minTime').value = secs2inputstr(minSecs);
minStr="&minTime=" + secs2inputstr(minSecs);
} }
if ( maxSecs > 0 )
minStr="&minTime=" + secs2dbstr(minSecs);
if ( maxSecs == 0 && minSecs == 0 ) { if ( maxSecs == 0 && minSecs == 0 ) {
minStr="&minTime=01/01/1950 12:00:00"; minStr="&minTime=01/01/1950T12:00:00";
maxStr="&maxTime=12/31/2035 12:00:00"; maxStr="&maxTime=12/31/2035T12:00:00";
} }
var intervalStr="&displayinterval=" + currentDisplayInterval.toString(); var intervalStr="&displayinterval=" + currentDisplayInterval.toString();
if ( minSecs && maxSecs ) { if ( minSecs && maxSecs ) {
@ -492,42 +485,42 @@ function clicknav(minSecs,maxSecs,arch,live) {// we use the current time if we c
if ( monitorZoomScale[monitorPtr[i]] < 0.99 || monitorZoomScale[monitorPtr[i]] > 1.01 ) // allow for some up/down changes and just treat as 1 of almost 1 if ( monitorZoomScale[monitorPtr[i]] < 0.99 || monitorZoomScale[monitorPtr[i]] > 1.01 ) // allow for some up/down changes and just treat as 1 of almost 1
zoomStr += "&z" + monitorPtr[i].toString() + "=" + monitorZoomScale[monitorPtr[i]].toFixed(2); zoomStr += "&z" + monitorPtr[i].toString() + "=" + monitorZoomScale[monitorPtr[i]].toFixed(2);
var uri = "?view=" + currentView + fitStr + groupStr + minStr + maxStr + currentStr + intervalStr + liveStr + zoomStr + "&scale=" + document.getElementById("scaleslider").value + "&speed=" + speeds[$j("#speedslider").value]; var uri = "?view=" + currentView + fitStr + groupStr + minStr + maxStr + currentStr + intervalStr + liveStr + zoomStr + "&scale=" + $j("#scaleslider")[0].value + "&speed=" + speeds[$j("#speedslider")[0].value];
window.location = uri; window.location = uri;
} } // end function clickNav
function lastHour() { function click_lastHour() {
var now = new Date() / 1000; var now = new Date() / 1000;
clicknav(now - 3600 + 1, now,1,0); clicknav(now - 3600 + 1, now,1,0);
} }
function lastEight() { function click_lastEight() {
var now = new Date() / 1000; var now = new Date() / 1000;
clicknav(now - 3600*8 + 1, now,1,0); clicknav(now - 3600*8 + 1, now,1,0);
} }
function zoomin() { function click_zoomin() {
rangeTimeSecs = parseInt(rangeTimeSecs / 2); rangeTimeSecs = parseInt(rangeTimeSecs / 2);
minTimeSecs = parseInt(currentTimeSecs - rangeTimeSecs/2); // this is the slider current time, we center on that minTimeSecs = parseInt(currentTimeSecs - rangeTimeSecs/2); // this is the slider current time, we center on that
maxTimeSecs = parseInt(currentTimeSecs + rangeTimeSecs/2); maxTimeSecs = parseInt(currentTimeSecs + rangeTimeSecs/2);
clicknav(minTimeSecs,maxTimeSecs,1,0); clicknav(minTimeSecs,maxTimeSecs,1,0);
} }
function zoomout() { function click_zoomout() {
rangeTimeSecs = parseInt(rangeTimeSecs * 2); rangeTimeSecs = parseInt(rangeTimeSecs * 2);
minTimeSecs = parseInt(currentTimeSecs - rangeTimeSecs/2); // this is the slider current time, we center on that minTimeSecs = parseInt(currentTimeSecs - rangeTimeSecs/2); // this is the slider current time, we center on that
maxTimeSecs = parseInt(currentTimeSecs + rangeTimeSecs/2); maxTimeSecs = parseInt(currentTimeSecs + rangeTimeSecs/2);
clicknav(minTimeSecs,maxTimeSecs,1,0); clicknav(minTimeSecs,maxTimeSecs,1,0);
} }
function panleft() { function click_panleft() {
minTimeSecs = parseInt(minTimeSecs - rangeTimeSecs/2); minTimeSecs = parseInt(minTimeSecs - rangeTimeSecs/2);
maxTimeSecs = minTimeSecs + rangeTimeSecs - 1; maxTimeSecs = minTimeSecs + rangeTimeSecs - 1;
clicknav(minTimeSecs,maxTimeSecs,1,0); clicknav(minTimeSecs,maxTimeSecs,1,0);
} }
function panright() { function click_panright() {
minTimeSecs = parseInt(minTimeSecs + rangeTimeSecs/2); minTimeSecs = parseInt(minTimeSecs + rangeTimeSecs/2);
maxTimeSecs = minTimeSecs + rangeTimeSecs - 1; maxTimeSecs = minTimeSecs + rangeTimeSecs - 1;
clicknav(minTimeSecs,maxTimeSecs,1,0); clicknav(minTimeSecs,maxTimeSecs,1,0);
} }
function allof() { function click_all_events() {
clicknav(0,0,1,0); clicknav(0,0,1,0);
} }
function allnon() { function allnon() {
@ -690,6 +683,10 @@ function clickMonitor(event,monId) {
return; return;
} }
function changeDateTime(e) {
e.form.submit();
}
// >>>>>>>>> Initialization that runs on window load by being at the bottom // >>>>>>>>> Initialization that runs on window load by being at the bottom
function initPage() { function initPage() {

View File

@ -1,7 +1,6 @@
var currentScale=<?php echo $defaultScale?>; var currentScale=<?php echo $defaultScale?>;
var liveMode=<?php echo $initialModeIsLive?>; var liveMode=<?php echo $initialModeIsLive?>;
console.log("Live mode?"+liveMode);
var fitMode=<?php echo $fitMode?>; var fitMode=<?php echo $fitMode?>;
var currentSpeed=<?php echo $speeds[$speedIndex]?>; // slider scale, which is only for replay and relative to real time var currentSpeed=<?php echo $speeds[$speedIndex]?>; // slider scale, which is only for replay and relative to real time
var speedIndex=<?php echo $speedIndex?>; var speedIndex=<?php echo $speedIndex?>;
@ -18,7 +17,7 @@ var eId = [];
var eStartSecs = []; var eStartSecs = [];
var eEndSecs = []; var eEndSecs = [];
var eventFrames = []; // this is going to presume all frames equal durationlength var eventFrames = []; // this is going to presume all frames equal durationlength
var groupStr=<?php if($group=="") echo '""'; else echo "\"&group=$group\""; ?>; var groupStr=<?php echo $group_id ? "'&group=$group_id'" : '""'; ?>;
<?php <?php
@ -178,8 +177,8 @@ foreach ( $monitors as $m ) {
$numMonitors += 1; $numMonitors += 1;
} }
echo "var numMonitors = $numMonitors;\n"; echo "var numMonitors = $numMonitors;\n";
echo "var minTimeSecs=" . $minTimeSecs . ";\n"; echo "var minTimeSecs='" . $minTimeSecs . "';\n";
echo "var maxTimeSecs=" . $maxTimeSecs . ";\n"; echo "var maxTimeSecs='" . $maxTimeSecs . "';\n";
echo "var rangeTimeSecs=" . ( $maxTimeSecs - $minTimeSecs + 1) . ";\n"; echo "var rangeTimeSecs=" . ( $maxTimeSecs - $minTimeSecs + 1) . ";\n";
if(isset($defaultCurrentTime)) if(isset($defaultCurrentTime))
echo "var currentTimeSecs=" . strtotime($defaultCurrentTime) . ";\n"; echo "var currentTimeSecs=" . strtotime($defaultCurrentTime) . ";\n";

View File

@ -31,7 +31,6 @@ $j(document).ready(function() {
// Save a new state // Save a new state
$j("#btnSave").click(function() { $j("#btnSave").click(function() {
StateStuff( 'save', undefined, $j("#newState").val() ); StateStuff( 'save', undefined, $j("#newState").val() );
}); });
// Change state // Change state
@ -53,7 +52,7 @@ $j(document).ready(function() {
$j.ajax({ $j.ajax({
type: 'POST', type: 'POST',
url: '/index.php', url: thisUrl,
data: formData, data: formData,
dataType: 'html', dataType: 'html',
enocde: true enocde: true

View File

@ -94,7 +94,7 @@ function previewEvent( eventId, frameId ) {
if ( event['frames'] ) { if ( event['frames'] ) {
if ( event['frames'][frameId] ) { if ( event['frames'][frameId] ) {
showEventDetail( event['frames'][frameId]['html'] ); showEventDetail( event['frames'][frameId]['html'] );
var imagePath = '/index.php?view=image&eid='+eventId+'&fid='+frameId; var imagePath = 'index.php?view=image&eid='+eventId+'&fid='+frameId;
var videoName = event.DefaultVideo; var videoName = event.DefaultVideo;
loadEventImage( imagePath, eventId, frameId, event.Width, event.Height, event.Frames/event.Length, videoName, event.Length, event.StartTime, monitors[event.MonitorId]); loadEventImage( imagePath, eventId, frameId, event.Width, event.Height, event.Frames/event.Length, videoName, event.Length, event.StartTime, monitors[event.MonitorId]);
return; return;

View File

@ -496,16 +496,20 @@ function getStreamCmdResponse( respObj, respText ) {
} }
} else { } else {
checkStreamForErrors("getStreamCmdResponse", respObj);//log them checkStreamForErrors("getStreamCmdResponse", respObj);//log them
if ( ! streamPause ) {
// Try to reload the image stream. // Try to reload the image stream.
var streamImg = document.getElementById('liveStream'); var streamImg = $('liveStream'+monitorId);
if ( streamImg ) if ( streamImg )
streamImg.src = streamImg.src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) )); streamImg.src = streamImg.src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) ));
} }
}
if ( ! streamPause ) {
var streamCmdTimeout = statusRefreshTimeout; var streamCmdTimeout = statusRefreshTimeout;
if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ) if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT )
streamCmdTimeout = streamCmdTimeout/5; streamCmdTimeout = streamCmdTimeout/5;
streamCmdTimer = streamCmdQuery.delay( streamCmdTimeout ); streamCmdTimer = streamCmdQuery.delay( streamCmdTimeout );
}
} }
var streamPause = false; var streamPause = false;
@ -556,10 +560,12 @@ function getStatusCmdResponse( respObj, respText ) {
} else } else
checkStreamForErrors("getStatusCmdResponse", respObj); checkStreamForErrors("getStatusCmdResponse", respObj);
if ( ! streamPause ) {
var statusCmdTimeout = statusRefreshTimeout; var statusCmdTimeout = statusRefreshTimeout;
if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ) if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT )
statusCmdTimeout = statusCmdTimeout/5; statusCmdTimeout = statusCmdTimeout/5;
statusCmdTimer = statusCmdQuery.delay( statusCmdTimeout ); statusCmdTimer = statusCmdQuery.delay( statusCmdTimeout );
}
} }
function statusCmdQuery() { function statusCmdQuery() {

View File

@ -51,11 +51,13 @@ if ( ! $Server ) {
$Server = array( 'Id' => '' ); $Server = array( 'Id' => '' );
} }
$monitor = null;
if ( ! empty($_REQUEST['mid']) ) { if ( ! empty($_REQUEST['mid']) ) {
$monitor = new Monitor( $_REQUEST['mid'] ); $monitor = new Monitor( $_REQUEST['mid'] );
if ( ZM_OPT_X10 ) if ( $monitor and ZM_OPT_X10 )
$x10Monitor = dbFetchOne( 'SELECT * FROM TriggersX10 WHERE MonitorId = ?', NULL, array($_REQUEST['mid']) ); $x10Monitor = dbFetchOne( 'SELECT * FROM TriggersX10 WHERE MonitorId = ?', NULL, array($_REQUEST['mid']) );
} else { }
if ( ! $monitor ) {
$nextId = getTableAutoInc( 'Monitors' ); $nextId = getTableAutoInc( 'Monitors' );
if ( isset( $_REQUEST['dupId'] ) ) { if ( isset( $_REQUEST['dupId'] ) ) {
@ -207,7 +209,8 @@ $sourceTypes = array(
'File' => translate('File'), 'File' => translate('File'),
'Ffmpeg' => translate('Ffmpeg'), 'Ffmpeg' => translate('Ffmpeg'),
'Libvlc' => translate('Libvlc'), 'Libvlc' => translate('Libvlc'),
'cURL' => 'cURL (HTTP(S) only)' 'cURL' => 'cURL (HTTP(S) only)',
'NVSocket' => translate('NVSocket')
); );
if ( !ZM_HAS_V4L ) if ( !ZM_HAS_V4L )
unset($sourceTypes['Local']); unset($sourceTypes['Local']);
@ -395,12 +398,12 @@ $Colours = array(
); );
$orientations = array( $orientations = array(
translate('Normal') => '0', '0' => translate('Normal'),
translate('RotateRight') => '90', '90' => translate('RotateRight'),
translate('Inverted') => '180', '180' => translate('Inverted'),
translate('RotateLeft') => '270', '270' => translate('RotateLeft'),
translate('FlippedHori') => 'hori', 'horz' => translate('FlippedHori'),
translate('FlippedVert') => 'vert' 'vert' => translate('FlippedVert')
); );
$deinterlaceopts = array( $deinterlaceopts = array(
@ -498,7 +501,7 @@ if ( canEdit( 'Monitors' ) ) {
<?php <?php
} // end if canEdit('Monitors') } // end if canEdit('Monitors')
?> ?>
<h2><?php echo translate('Monitor') ?> - <?php echo validHtmlStr($monitor->Name()) ?><?php if ( !empty($monitor->Id()) ) { ?> (<?php echo $monitor->Id()?>)<?php } ?></h2> <h2><?php echo translate('Monitor') ?> - <?php echo validHtmlStr($monitor->Name()) ?><?php if ( $monitor->Id() ) { ?> (<?php echo $monitor->Id()?>)<?php } ?></h2>
</div> </div>
<div id="content"> <div id="content">
<ul class="tabList"> <ul class="tabList">
@ -598,7 +601,7 @@ if ( $tab != 'storage' ) {
<input type="hidden" name="newMonitor[RecordAudio]" value="<?php echo validHtmlStr($monitor->RecordAudio()) ?>"/> <input type="hidden" name="newMonitor[RecordAudio]" value="<?php echo validHtmlStr($monitor->RecordAudio()) ?>"/>
<?php <?php
} }
if ( $tab != 'source' || ($monitor->Type()!= 'Remote' && $monitor->Protocol()!= 'rtsp') ) { if ( $tab != 'source' || ($monitor->Type() != 'Remote' && $monitor->Protocol()!= 'rtsp') ) {
?> ?>
<input type="hidden" name="newMonitor[RTSPDescribe]" value="<?php echo validHtmlStr($monitor->RTSPDescribe()) ?>"/> <input type="hidden" name="newMonitor[RTSPDescribe]" value="<?php echo validHtmlStr($monitor->RTSPDescribe()) ?>"/>
<?php <?php
@ -702,19 +705,19 @@ switch ( $tab ) {
} }
?> ?>
</select></td></tr> </select></td></tr>
<tr><td><?php echo translate('Enabled') ?></td><td><input type="checkbox" name="newMonitor[Enabled]" value="1"<?php if ( !empty($monitor->Enabled()) ) { ?> checked="checked"<?php } ?>/></td></tr> <tr><td><?php echo translate('Enabled') ?></td><td><input type="checkbox" name="newMonitor[Enabled]" value="1"<?php if ( $monitor->Enabled() ) { ?> checked="checked"<?php } ?>/></td></tr>
<tr> <tr>
<td><?php echo translate('LinkedMonitors') ?></td> <td><?php echo translate('LinkedMonitors') ?></td>
<td> <td>
<select name="monitorIds" size="4" multiple="multiple" onchange="updateLinkedMonitors( this )"> <select name="monitorIds" size="4" multiple="multiple" onchange="updateLinkedMonitors( this )">
<?php <?php
$monitors = dbFetchAll( 'select Id,Name from Monitors order by Sequence asc' ); $monitors = dbFetchAll( 'select Id,Name from Monitors order by Sequence asc' );
if ( !empty($monitor->LinkedMonitors()) ) if ( $monitor->LinkedMonitors() )
$monitorIds = array_flip( explode( ',', $monitor->LinkedMonitors()) ); $monitorIds = array_flip( explode( ',', $monitor->LinkedMonitors()) );
else else
$monitorIds = array(); $monitorIds = array();
foreach ( $monitors as $linked_monitor ) { foreach ( $monitors as $linked_monitor ) {
if ( (empty($monitor->Id()) || ($monitor->Id()!= $linked_monitor['Id'])) && visibleMonitor( $linked_monitor['Id'] ) ) { if ( (!$monitor->Id() || ($monitor->Id()!= $linked_monitor['Id'])) && visibleMonitor( $linked_monitor['Id'] ) ) {
?> ?>
<option value="<?php echo $linked_monitor['Id'] ?>"<?php if ( array_key_exists( $linked_monitor['Id'], $monitorIds ) ) { ?> selected="selected"<?php } ?>><?php echo validHtmlStr($linked_monitor['Name']) ?></option> <option value="<?php echo $linked_monitor['Id'] ?>"<?php if ( array_key_exists( $linked_monitor['Id'], $monitorIds ) ) { ?> selected="selected"<?php } ?>><?php echo validHtmlStr($linked_monitor['Name']) ?></option>
<?php <?php
@ -726,7 +729,7 @@ switch ( $tab ) {
</tr> </tr>
<tr><td><?php echo translate('AnalysisFPS') ?></td><td><input type="text" name="newMonitor[AnalysisFPS]" value="<?php echo validHtmlStr($monitor->AnalysisFPS()) ?>" size="6"/></td></tr> <tr><td><?php echo translate('AnalysisFPS') ?></td><td><input type="text" name="newMonitor[AnalysisFPS]" value="<?php echo validHtmlStr($monitor->AnalysisFPS()) ?>" size="6"/></td></tr>
<?php <?php
if ( $monitor->Type() != 'Local' && $monitor->Type() != 'File' ) { if ( $monitor->Type() != 'Local' && $monitor->Type() != 'File' && $monitor->Type() != 'NVSocket' ) {
?> ?>
<tr> <tr>
<td><?php echo translate('MaximumFPS') ?>&nbsp;(<?php echo makePopupLink('?view=optionhelp&amp;option=OPTIONS_MAXFPS', 'zmOptionHelp', 'optionhelp', '?' ) ?>)</td> <td><?php echo translate('MaximumFPS') ?>&nbsp;(<?php echo makePopupLink('?view=optionhelp&amp;option=OPTIONS_MAXFPS', 'zmOptionHelp', 'optionhelp', '?' ) ?>)</td>
@ -822,11 +825,14 @@ switch ( $tab ) {
</td></tr> </td></tr>
<tr><td><?php echo translate('V4LCapturesPerFrame') ?></td><td><input type="number" name="newMonitor[V4LCapturesPerFrame]" value="<?php echo $monitor->V4LCapturesPerFrame()?>"/></td></tr> <tr><td><?php echo translate('V4LCapturesPerFrame') ?></td><td><input type="number" name="newMonitor[V4LCapturesPerFrame]" value="<?php echo $monitor->V4LCapturesPerFrame()?>"/></td></tr>
<?php <?php
} elseif ( $monitor->Type() == 'Remote' ) {
} else if ( $monitor->Type() == 'NVSocket' ) {
include('monitor_source_nvsocket.php');
} else if ( $monitor->Type() == 'Remote' ) {
?> ?>
<tr><td><?php echo translate('RemoteProtocol') ?></td><td><?php echo htmlSelect( "newMonitor[Protocol]", $remoteProtocols, $monitor->Protocol(), "updateMethods( this );if(this.value=='rtsp'){\$('RTSPDescribe').setStyle('display','table-row');}else{\$('RTSPDescribe').hide();}" ); ?></td></tr> <tr><td><?php echo translate('RemoteProtocol') ?></td><td><?php echo htmlSelect( "newMonitor[Protocol]", $remoteProtocols, $monitor->Protocol(), "updateMethods( this );if(this.value=='rtsp'){\$('RTSPDescribe').setStyle('display','table-row');}else{\$('RTSPDescribe').hide();}" ); ?></td></tr>
<?php <?php
if ( empty($monitor->Protocol()) || $monitor->Protocol() == 'http' ) { if ( !$monitor->Protocol() || $monitor->Protocol() == 'http' ) {
?> ?>
<tr><td><?php echo translate('RemoteMethod') ?></td><td><?php echo htmlSelect( "newMonitor[Method]", $httpMethods, $monitor->Method() ); ?></td></tr> <tr><td><?php echo translate('RemoteMethod') ?></td><td><?php echo htmlSelect( "newMonitor[Method]", $httpMethods, $monitor->Method() ); ?></td></tr>
<?php <?php
@ -837,7 +843,7 @@ switch ( $tab ) {
} }
?> ?>
<tr><td><?php echo translate('RemoteHostName') ?></td><td><input type="text" name="newMonitor[Host]" value="<?php echo validHtmlStr($monitor->Host()) ?>" size="36"/></td></tr> <tr><td><?php echo translate('RemoteHostName') ?></td><td><input type="text" name="newMonitor[Host]" value="<?php echo validHtmlStr($monitor->Host()) ?>" size="36"/></td></tr>
<tr><td><?php echo translate('RemoteHostPort') ?></td><td><input type="text" name="newMonitor[Port]" value="<?php echo validHtmlStr($monitor->Port()) ?>" size="6"/></td></tr> <tr><td><?php echo translate('RemoteHostPort') ?></td><td><input type="number" name="newMonitor[Port]" value="<?php echo validHtmlStr($monitor->Port()) ?>" size="6"/></td></tr>
<tr><td><?php echo translate('RemoteHostPath') ?></td><td><input type="text" name="newMonitor[Path]" value="<?php echo validHtmlStr($monitor->Path()) ?>" size="36"/></td></tr> <tr><td><?php echo translate('RemoteHostPath') ?></td><td><input type="text" name="newMonitor[Path]" value="<?php echo validHtmlStr($monitor->Path()) ?>" size="36"/></td></tr>
<?php <?php
} else if ( $monitor->Type() == 'File' ) { } else if ( $monitor->Type() == 'File' ) {
@ -857,14 +863,16 @@ switch ( $tab ) {
<tr><td><?php echo translate('Options') ?>&nbsp;(<?php echo makePopupLink( '?view=optionhelp&amp;option=OPTIONS_'.strtoupper($monitor->Type()), 'zmOptionHelp', 'optionhelp', '?' ) ?>)</td><td><input type="text" name="newMonitor[Options]" value="<?php echo validHtmlStr($monitor->Options()) ?>" size="36"/></td></tr> <tr><td><?php echo translate('Options') ?>&nbsp;(<?php echo makePopupLink( '?view=optionhelp&amp;option=OPTIONS_'.strtoupper($monitor->Type()), 'zmOptionHelp', 'optionhelp', '?' ) ?>)</td><td><input type="text" name="newMonitor[Options]" value="<?php echo validHtmlStr($monitor->Options()) ?>" size="36"/></td></tr>
<?php <?php
} }
if ( $monitor->Type() != 'NVSocket' ) {
?> ?>
<tr><td><?php echo translate('TargetColorspace') ?></td><td><select name="newMonitor[Colours]"><?php foreach ( $Colours as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->Colours()) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr> <tr><td><?php echo translate('TargetColorspace') ?></td><td><select name="newMonitor[Colours]"><?php foreach ( $Colours as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->Colours()) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
<tr><td><?php echo translate('CaptureWidth') ?> (<?php echo translate('Pixels') ?>)</td><td><input type="text" name="newMonitor[Width]" value="<?php echo validHtmlStr($monitor->Width()) ?>" size="4" onkeyup="updateMonitorDimensions(this);"/></td></tr> <tr><td><?php echo translate('CaptureWidth') ?> (<?php echo translate('Pixels') ?>)</td><td><input type="text" name="newMonitor[Width]" value="<?php echo validHtmlStr($monitor->Width()) ?>" size="4" onkeyup="updateMonitorDimensions(this);"/></td></tr>
<tr><td><?php echo translate('CaptureHeight') ?> (<?php echo translate('Pixels') ?>)</td><td><input type="text" name="newMonitor[Height]" value="<?php echo validHtmlStr($monitor->Height()) ?>" size="4" onkeyup="updateMonitorDimensions(this);"/></td></tr> <tr><td><?php echo translate('CaptureHeight') ?> (<?php echo translate('Pixels') ?>)</td><td><input type="text" name="newMonitor[Height]" value="<?php echo validHtmlStr($monitor->Height()) ?>" size="4" onkeyup="updateMonitorDimensions(this);"/></td></tr>
<tr><td><?php echo translate('PreserveAspect') ?></td><td><input type="checkbox" name="preserveAspectRatio" value="1"/></td></tr> <tr><td><?php echo translate('PreserveAspect') ?></td><td><input type="checkbox" name="preserveAspectRatio" value="1"/></td></tr>
<tr><td><?php echo translate('Orientation') ?></td><td><select name="newMonitor[Orientation]"><?php foreach ( $orientations as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->Orientation()) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr> <tr><td><?php echo translate('Orientation') ?></td><td><?php echo htmlselect( 'newMonitor[Orientation]', $orientations, $monitor->Orientation() );?></td></tr>
<?php <?php
if ( $monitor->Type() == 'Local' ) { }
if ( $monitor->Type() == 'Local' ) {
?> ?>
<tr><td><?php echo translate('Deinterlacing') ?></td><td><select name="newMonitor[Deinterlacing]"><?php foreach ( $deinterlaceopts_v4l2 as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->Deinterlacing()) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr> <tr><td><?php echo translate('Deinterlacing') ?></td><td><select name="newMonitor[Deinterlacing]"><?php foreach ( $deinterlaceopts_v4l2 as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->Deinterlacing()) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
<?php <?php
@ -877,7 +885,7 @@ switch ( $tab ) {
<?php <?php
if ( $monitor->Type() == 'Remote' ) { if ( $monitor->Type() == 'Remote' ) {
?> ?>
<tr id="RTSPDescribe"<?php if ( $monitor->Protocol()!= 'rtsp' ) { echo ' style="display:none;"'; } ?>><td><?php echo translate('RTSPDescribe') ?>&nbsp;(<?php echo makePopupLink( '?view=optionhelp&amp;option=OPTIONS_RTSPDESCRIBE', 'zmOptionHelp', 'optionhelp', '?' ) ?>) </td><td><input type="checkbox" name="newMonitor[RTSPDescribe]" value="1"<?php if ( !empty($monitor->RTSPDescribe()) ) { ?> checked="checked"<?php } ?>/></td></tr> <tr id="RTSPDescribe"<?php if ( $monitor->Protocol()!= 'rtsp' ) { echo ' style="display:none;"'; } ?>><td><?php echo translate('RTSPDescribe') ?>&nbsp;(<?php echo makePopupLink( '?view=optionhelp&amp;option=OPTIONS_RTSPDESCRIBE', 'zmOptionHelp', 'optionhelp', '?' ) ?>) </td><td><input type="checkbox" name="newMonitor[RTSPDescribe]" value="1"<?php if ( $monitor->RTSPDescribe() ) { ?> checked="checked"<?php } ?>/></td></tr>
<?php <?php
} }
break; break;
@ -887,7 +895,7 @@ switch ( $tab ) {
<tr><td><?php echo translate('SaveJPEGs') ?></td><td><select name="newMonitor[SaveJPEGs]"><?php foreach ( $savejpegopts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->SaveJPEGs() ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr> <tr><td><?php echo translate('SaveJPEGs') ?></td><td><select name="newMonitor[SaveJPEGs]"><?php foreach ( $savejpegopts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->SaveJPEGs() ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
<tr><td><?php echo translate('VideoWriter') ?></td><td><select name="newMonitor[VideoWriter]"><?php foreach ( $videowriteropts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->VideoWriter() ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr> <tr><td><?php echo translate('VideoWriter') ?></td><td><select name="newMonitor[VideoWriter]"><?php foreach ( $videowriteropts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->VideoWriter() ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
<tr><td><?php echo translate('OptionalEncoderParam') ?></td><td><textarea name="newMonitor[EncoderParameters]" rows="4" cols="36"><?php echo validHtmlStr($monitor->EncoderParameters()) ?></textarea></td></tr> <tr><td><?php echo translate('OptionalEncoderParam') ?></td><td><textarea name="newMonitor[EncoderParameters]" rows="4" cols="36"><?php echo validHtmlStr($monitor->EncoderParameters()) ?></textarea></td></tr>
<tr><td><?php echo translate('RecordAudio') ?></td><td><input type="checkbox" name="newMonitor[RecordAudio]" value="1"<?php if ( !empty($monitor->RecordAudio()) ) { ?> checked="checked"<?php } ?>/></td></tr> <tr><td><?php echo translate('RecordAudio') ?></td><td><input type="checkbox" name="newMonitor[RecordAudio]" value="1"<?php if ( $monitor->RecordAudio() ) { ?> checked="checked"<?php } ?>/></td></tr>
<?php <?php
break; break;
case 'timestamp' : case 'timestamp' :
@ -915,12 +923,12 @@ switch ( $tab ) {
case 'control' : case 'control' :
{ {
?> ?>
<tr><td><?php echo translate('Controllable') ?></td><td><input type="checkbox" name="newMonitor[Controllable]" value="1"<?php if ( !empty($monitor->Controllable()) ) { ?> checked="checked"<?php } ?>/></td></tr> <tr><td><?php echo translate('Controllable') ?></td><td><input type="checkbox" name="newMonitor[Controllable]" value="1"<?php if ( $monitor->Controllable() ) { ?> checked="checked"<?php } ?>/></td></tr>
<tr><td><?php echo translate('ControlType') ?></td><td><?php echo buildSelect( "newMonitor[ControlId]", $controlTypes, 'loadLocations( this )' ); ?><?php if ( canEdit( 'Control' ) ) { ?>&nbsp;<a href="#" onclick="createPopup( '?view=controlcaps', 'zmControlCaps', 'controlcaps' );"><?php echo translate('Edit') ?></a><?php } ?></td></tr> <tr><td><?php echo translate('ControlType') ?></td><td><?php echo buildSelect( "newMonitor[ControlId]", $controlTypes, 'loadLocations( this )' ); ?><?php if ( canEdit( 'Control' ) ) { ?>&nbsp;<a href="#" onclick="createPopup( '?view=controlcaps', 'zmControlCaps', 'controlcaps' );"><?php echo translate('Edit') ?></a><?php } ?></td></tr>
<tr><td><?php echo translate('ControlDevice') ?></td><td><input type="text" name="newMonitor[ControlDevice]" value="<?php echo validHtmlStr($monitor->ControlDevice()) ?>" size="32"/></td></tr> <tr><td><?php echo translate('ControlDevice') ?></td><td><input type="text" name="newMonitor[ControlDevice]" value="<?php echo validHtmlStr($monitor->ControlDevice()) ?>" size="32"/></td></tr>
<tr><td><?php echo translate('ControlAddress') ?></td><td><input type="text" name="newMonitor[ControlAddress]" value="<?php echo validHtmlStr($monitor->ControlAddress()) ?>" size="32"/></td></tr> <tr><td><?php echo translate('ControlAddress') ?></td><td><input type="text" name="newMonitor[ControlAddress]" value="<?php echo validHtmlStr($monitor->ControlAddress()) ?>" size="32"/></td></tr>
<tr><td><?php echo translate('AutoStopTimeout') ?></td><td><input type="text" name="newMonitor[AutoStopTimeout]" value="<?php echo validHtmlStr($monitor->AutoStopTimeout()) ?>" size="4"/></td></tr> <tr><td><?php echo translate('AutoStopTimeout') ?></td><td><input type="text" name="newMonitor[AutoStopTimeout]" value="<?php echo validHtmlStr($monitor->AutoStopTimeout()) ?>" size="4"/></td></tr>
<tr><td><?php echo translate('TrackMotion') ?></td><td><input type="checkbox" name="newMonitor[TrackMotion]" value="1"<?php if ( !empty($monitor->TrackMotion()) ) { ?> checked="checked"<?php } ?>/></td></tr> <tr><td><?php echo translate('TrackMotion') ?></td><td><input type="checkbox" name="newMonitor[TrackMotion]" value="1"<?php if ( $monitor->TrackMotion() ) { ?> checked="checked"<?php } ?>/></td></tr>
<?php <?php
$return_options = array( $return_options = array(
'-1' => translate('None'), '-1' => translate('None'),
@ -987,7 +995,7 @@ switch ( $tab ) {
</tr> </tr>
<tr> <tr>
<td><?php echo translate('Exif') ?>&nbsp;(<?php echo makePopupLink( '?view=optionhelp&amp;option=OPTIONS_EXIF', 'zmOptionHelp', 'optionhelp', '?' ) ?>) </td> <td><?php echo translate('Exif') ?>&nbsp;(<?php echo makePopupLink( '?view=optionhelp&amp;option=OPTIONS_EXIF', 'zmOptionHelp', 'optionhelp', '?' ) ?>) </td>
<td><input type="checkbox" name="newMonitor[Exif]" value="1"<?php if ( !empty($monitor->Exif()) ) { ?> checked="checked"<?php } ?>/></td> <td><input type="checkbox" name="newMonitor[Exif]" value="1"<?php if ( $monitor->Exif() ) { ?> checked="checked"<?php } ?>/></td>
</tr> </tr>
<?php <?php
break; break;
@ -1000,7 +1008,6 @@ switch ( $tab ) {
<input type="submit" value="<?php echo translate('Save') ?>"<?php if ( !canEdit( 'Monitors' ) ) { ?> disabled="disabled"<?php } ?>/> <input type="submit" value="<?php echo translate('Save') ?>"<?php if ( !canEdit( 'Monitors' ) ) { ?> disabled="disabled"<?php } ?>/>
<input type="button" value="<?php echo translate('Cancel') ?>" onclick="closeWindow()"/> <input type="button" value="<?php echo translate('Cancel') ?>" onclick="closeWindow()"/>
</div> </div>
</form> </form>
</div> </div>
</div> </div>

View File

@ -18,21 +18,11 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// //
if ( !canView( 'Stream' ) ) { if ( !canView('Stream') ) {
$view = 'error'; $view = 'error';
return; return;
} }
require_once( 'includes/Monitor.php' );
$groupSql = '';
if ( !empty($_REQUEST['group']) ) {
$row = dbFetchOne( 'SELECT * FROM Groups WHERE Id = ?', NULL, array($_REQUEST['group']) );
$sql = "SELECT * FROM Monitors WHERE Function != 'None' AND find_in_set( Id, '".$row['MonitorIds']."' ) ORDER BY Sequence";
} else {
$sql = "SELECT * FROM Monitors WHERE Function != 'None' ORDER BY Sequence";
}
$showControl = false; $showControl = false;
$showZones = false; $showZones = false;
if ( isset( $_REQUEST['showZones'] ) ) { if ( isset( $_REQUEST['showZones'] ) ) {
@ -40,7 +30,6 @@ if ( isset( $_REQUEST['showZones'] ) ) {
$showZones = true; $showZones = true;
} }
} }
$monitors = array();
$widths = array( $widths = array(
'' => 'auto', '' => 'auto',
160 => 160, 160 => 160,
@ -68,26 +57,6 @@ if ( isset( $_REQUEST['scale'] ) ) {
if ( ! $scale ) if ( ! $scale )
$scale = 100; $scale = 100;
foreach( dbFetchAll( $sql ) as $row ) {
if ( !visibleMonitor( $row['Id'] ) ) {
continue;
}
$row['Scale'] = $scale;
$row['PopupScale'] = reScale( SCALE_BASE, $row['DefaultScale'], ZM_WEB_DEFAULT_SCALE );
if ( ZM_OPT_CONTROL && $row['ControlId'] && $row['Controllable'] )
$showControl = true;
$row['connKey'] = generateConnKey();
$monitors[] = new Monitor( $row );
if ( ! isset( $widths[$row['Width']] ) ) {
$widths[$row['Width']] = $row['Width'];
}
if ( ! isset( $heights[$row['Height']] ) ) {
$heights[$row['Height']] = $row['Height'];
}
} # end foreach Monitor
$focusWindow = true; $focusWindow = true;
$layouts = array( $layouts = array(
@ -114,10 +83,42 @@ else
if ( $scale ) if ( $scale )
$options['scale'] = $scale; $options['scale'] = $scale;
ob_start();
# This will end up with the group_id of the deepest selection
$group_id = Group::get_group_dropdowns();
$group_dropdowns = ob_get_contents();
ob_end_clean();
$groupSql = Group::get_group_sql( $group_id );
$monitors = array();
$sql = "SELECT * FROM Monitors WHERE Function != 'None'";
if ( $groupSql ) { $sql .= ' AND ' . $groupSql; };
$sql .= 'ORDER BY Sequence';
foreach( dbFetchAll( $sql ) as $row ) {
if ( !visibleMonitor( $row['Id'] ) ) {
continue;
}
$row['Scale'] = $scale;
$row['PopupScale'] = reScale( SCALE_BASE, $row['DefaultScale'], ZM_WEB_DEFAULT_SCALE );
if ( ZM_OPT_CONTROL && $row['ControlId'] && $row['Controllable'] )
$showControl = true;
$row['connKey'] = generateConnKey();
$monitors[] = new Monitor( $row );
if ( ! isset( $widths[$row['Width']] ) ) {
$widths[$row['Width']] = $row['Width'];
}
if ( ! isset( $heights[$row['Height']] ) ) {
$heights[$row['Height']] = $row['Height'];
}
} # end foreach Monitor
xhtmlHeaders(__FILE__, translate('Montage') ); xhtmlHeaders(__FILE__, translate('Montage') );
?> ?>
<body> <body>
<div id="page"> <div id="page">
<?php echo getNavBarHTML() ?>
<div id="header"> <div id="header">
<div id="headerButtons"> <div id="headerButtons">
<?php <?php
@ -136,10 +137,13 @@ if ( $showZones ) {
<?php <?php
} }
?> ?>
<a href="#" onclick="closeWindow()"><?php echo translate('Close') ?></a>
</div> </div>
<h2><?php echo translate('Montage') ?></h2>
<div id="headerControl"> <div id="headerControl">
<span id="groupControl"><label><?php echo translate('Group') ?>:</label>
<?php
echo $group_dropdowns;
?>
</span>
<span id="widthControl"><label><?php echo translate('Width') ?>:</label><?php echo htmlSelect( 'width', $widths, $options['width'], 'changeSize(this);' ); ?></span> <span id="widthControl"><label><?php echo translate('Width') ?>:</label><?php echo htmlSelect( 'width', $widths, $options['width'], 'changeSize(this);' ); ?></span>
<span id="heightControl"><label><?php echo translate('Height') ?>:</label><?php echo htmlSelect( 'height', $heights, $options['height'], 'changeSize(this);' ); ?></span> <span id="heightControl"><label><?php echo translate('Height') ?>:</label><?php echo htmlSelect( 'height', $heights, $options['height'], 'changeSize(this);' ); ?></span>
<span id="scaleControl"><label><?php echo translate('Scale') ?>:</label><?php echo htmlSelect( 'scale', $scales, $scale, 'changeScale(this);' ); ?></span> <span id="scaleControl"><label><?php echo translate('Scale') ?>:</label><?php echo htmlSelect( 'scale', $scales, $scale, 'changeScale(this);' ); ?></span>
@ -224,5 +228,4 @@ foreach ( $monitors as $monitor ) {
</div> </div>
</div> </div>
</div> </div>
</body> <?php xhtmlFooter() ?>
</html>

View File

@ -48,68 +48,29 @@
// It takes very high bandwidth to the server, and a pretty fast client to keep up with the image rate. To reduce the rate // It takes very high bandwidth to the server, and a pretty fast client to keep up with the image rate. To reduce the rate
// change the playback slider to 0 and then it does not try to play at the same time it is scrubbing. // change the playback slider to 0 and then it does not try to play at the same time it is scrubbing.
// //
// Jul 23 2015 update:
// - Correct problem propagating selected playback speed
// - Change from blank monitor to specific message about no data in times with no recording
// - Enlarge and better center fonts on labels for lines.
// - Add support for monitor groups in selecting criteria from console
// - Fix some no-update conditions when playback was off but scale changed or refreshed.
// - Added translate call around buttons so as to facilitate possible translations later
// - Removed range from/to labels on very small graphs to keep from overlapping slider
// - Changed initial (from other page) position of slider to be in the middle to be more obvious
//
// Jul 29 2015 update
// - Add live mode shots from cameras via single frame pull mode
// - Added dynamic refresh rate based on how fast we can upload images
// - Closed some gaps in playback frames due to time rounding in retrieval.
// - Consolidated frame in-memory records to contiguous time rather than individual frame-seconds (still requires a good deal of browser memory)
// - Took out a lot of the integral second rounding so that it works better at subsequent replay speeds
//
// Jul 30 2015 update
// - Smoother adjustment of frame rate, fixed upper/lower limits (caching can cause runaway) (and a display, probably temporary, at the bottom)
// - Change to using index.php?view= instead of direct access so image access is authenticated
// - Add fractional speed for replay, and non-linear speed slider, and update current setting as slider moves (not when done)
// - Experimenting with a black background for monitors (this should be replaced with proper CSS later)
//
// Aug 02, 2015 update
// - Add max fit, make it default
// - Remove timeline in live mode, and restore when switched back (live button becomes toggle)
// - Add +/- zooms to individual monitors so you can adjust size, persist across reload buttons (only)
// - Change default to 1 hour and live mode (reduce workload on initial load, let people ask for huge history amounts)
// - Since this may be run as a standalone window for shortcuts, etc., add a "console" link to get back to the console
//
// August 6, 2015 update
// - Fix regression on linkage to events when starting and staying in live mode
// - Remove zoom/pan buttons in live mode as they are meaningless
// - Change "fit" to a button, and remove scale when fit is in use (this means fit/live has no sliders)
//
// August 8, 2015 update:
// - Optimize events query to significantly decrease load times
// - Consolidate frames to 10 seconds not 1 for faster load and less memory usage
// - Replace graphic image for no-data with text-on-canvas (faster)
// - Correct sorting issue related to normalized scale so biggest goes to top left more reliably
// - Corrections to Safari which won't support inline-flex (thanks Apple, really?!)
//
// August 9, 2015 updates:
// - Add auth tokens to zms call for those using authorization
//
if ( !canView( 'Events' ) ) { if ( !canView( 'Events' ) ) {
$view = 'error'; $view = 'error';
return; return;
} }
require_once( 'includes/Monitor.php' ); ob_start();
# This will end up with the group_id of the deepest selection
$group_id = Group::get_group_dropdowns();
$group_dropdowns = ob_get_contents();
ob_end_clean();
# FIXME THere is no way to select group at this time. $groupSql = Group::get_group_sql( $group_id );
if ( !empty($_REQUEST['group']) ) {
$group = $_REQUEST['group']; session_start();
$row = dbFetchOne( 'SELECT * FROM Groups WHERE Id = ?', NULL, array($_REQUEST['group']) ); foreach ( array('minTime','maxTime') as $var ) {
$monitorsSql = "SELECT * FROM Monitors WHERE Function != 'None' AND find_in_set( Id, '".$row['MonitorIds']."' ) "; if ( isset( $_REQUEST[$var] ) ) {
} else { $_SESSION[$var] = $_REQUEST[$var];
$monitorsSql = "SELECT * FROM Monitors WHERE Function != 'None'";
$group = '';
} }
}
session_write_close();
$monitorsSql = "SELECT * FROM Monitors WHERE Function != 'None'" . ( $groupSql ? ' AND ' . $groupSql : '');
// Note that this finds incomplete events as well, and any frame records written, but still cannot "see" to the end frame // Note that this finds incomplete events as well, and any frame records written, but still cannot "see" to the end frame
// if the bulk record has not been written - to be able to include more current frames reduce bulk frame sizes (event size can be large) // if the bulk record has not been written - to be able to include more current frames reduce bulk frame sizes (event size can be large)
@ -153,8 +114,9 @@ if ( ! empty( $user['MonitorIds'] ) ) {
if ( !isset($_REQUEST['minTime']) && !isset($_REQUEST['maxTime']) ) { if ( !isset($_REQUEST['minTime']) && !isset($_REQUEST['maxTime']) ) {
$maxTime = strftime("%c",time()); $time = time();
$minTime = strftime("%c",time() - 3600); $maxTime = strftime("%FT%T",$time);
$minTime = strftime("%FT%T",$time - 3600);
} }
if ( isset($_REQUEST['minTime']) ) if ( isset($_REQUEST['minTime']) )
$minTime = validHtmlStr($_REQUEST['minTime']); $minTime = validHtmlStr($_REQUEST['minTime']);
@ -197,14 +159,13 @@ for ( $i = 0; $i < count($speeds); $i++ ) {
if ( isset($_REQUEST['current']) ) if ( isset($_REQUEST['current']) )
$defaultCurrentTime = validHtmlStr($_REQUEST['current']); $defaultCurrentTime = validHtmlStr($_REQUEST['current']);
$initialModeIsLive = 1; $initialModeIsLive = 1;
if ( isset($_REQUEST['live']) && $_REQUEST['live']=='0' ) if ( isset($_REQUEST['live']) && $_REQUEST['live']=='0' )
$initialModeIsLive=0; $initialModeIsLive=0;
$initialDisplayInterval=1000; $initialDisplayInterval = 1000;
if ( isset($_REQUEST['displayinterval']) ) if ( isset($_REQUEST['displayinterval']) )
$initialDisplayInterval=validHtmlStr($_REQUEST['displayinterval']); $initialDisplayInterval = validHtmlStr($_REQUEST['displayinterval']);
$eventsSql .= ' GROUP BY E.Id,E.Name,E.StartTime,E.Length,E.Frames,E.MaxScore,E.Cause,E.Notes,E.Archived,E.MonitorId'; $eventsSql .= ' GROUP BY E.Id,E.Name,E.StartTime,E.Length,E.Frames,E.MaxScore,E.Cause,E.Notes,E.Archived,E.MonitorId';
@ -230,18 +191,19 @@ foreach( dbFetchAll( $monitorsSql ) as $row ) {
xhtmlHeaders(__FILE__, translate('MontageReview') ); xhtmlHeaders(__FILE__, translate('MontageReview') );
?> ?>
<style>
input[type=range]::-ms-tooltip {
display: none;
}
</style>
<body> <body>
<div id="page"> <div id="page">
<?php echo getNavBarHTML() ?>
<form action="<?php echo $_SERVER['PHP_SELF'] ?>" method="get">
<input type="hidden" name="view" value="montagereview"/>
<div id="header"> <div id="header">
<div id="headerButtons"> <div id="headerControl">
<a href="#" onclick="closeWindow();"><?php echo translate('Close') ?></a> <span id="groupControl"><label><?php echo translate('Group') ?>:</label>
</div> <?php echo $group_dropdowns; ?>
<h2><?php echo translate('MontageReview') ?></h2> </span>
<div id="DateTimeDiv">
<input type="datetime-local" name="minTime" id="minTime" value="<?php echo preg_replace('/ /', 'T', $minTime ) ?>" onchange="changeDateTime(this);"> to
<input type="datetime-local" name="maxTime" id="maxTime" value="<?php echo preg_replace('/ /', 'T', $maxTime ) ?>" onchange="changeDateTime(this);">
</div> </div>
<div id="ScaleDiv"> <div id="ScaleDiv">
<label for="scaleslider"><?php echo translate('Scale')?></label> <label for="scaleslider"><?php echo translate('Scale')?></label>
@ -254,15 +216,15 @@ input[type=range]::-ms-tooltip {
<span id="speedslideroutput"><?php echo $speeds[$speedIndex] ?> fps</span> <span id="speedslideroutput"><?php echo $speeds[$speedIndex] ?> fps</span>
</div> </div>
<div style="display: inline-flex; border: 1px solid black; flex-flow: row wrap;"> <div style="display: inline-flex; border: 1px solid black; flex-flow: row wrap;">
<button type="button" id="panleft" onclick="panleft();" >&lt; <?php echo translate('Pan') ?></button> <button type="button" id="panleft" onclick="click_panleft();" >&lt; <?php echo translate('Pan') ?></button>
<button type="button" id="zoomin" onclick="zoomin();" ><?php echo translate('In +') ?></button> <button type="button" id="zoomin" onclick="click_zoomin();" ><?php echo translate('In +') ?></button>
<button type="button" id="zoomout" onclick="zoomout();" ><?php echo translate('Out -') ?></button> <button type="button" id="zoomout" onclick="click_zoomout();" ><?php echo translate('Out -') ?></button>
<button type="button" id="lasteight" onclick="lastEight();" ><?php echo translate('8 Hour') ?></button> <button type="button" id="lasteight" onclick="click_lastEight();" ><?php echo translate('8 Hour') ?></button>
<button type="button" id="lasthour" onclick="lastHour();" ><?php echo translate('1 Hour') ?></button> <button type="button" id="lasthour" onclick="click_lastHour();" ><?php echo translate('1 Hour') ?></button>
<button type="button" id="allof" onclick="allof();" ><?php echo translate('All Events') ?></button> <button type="button" id="allof" onclick="click_all_events();" ><?php echo translate('All Events') ?></button>
<button type="button" id="live" onclick="setLive(1-liveMode);"><?php echo translate('Live') ?></button> <button type="button" id="live" onclick="setLive(1-liveMode);"><?php echo translate('Live') ?></button>
<button type="button" id="fit" onclick="setFit(1-fitMode);" ><?php echo translate('Fit') ?></button> <button type="button" id="fit" onclick="setFit(1-fitMode);" ><?php echo translate('Fit') ?></button>
<button type="button" id="panright" onclick="panright();" ><?php echo translate('Pan') ?> &gt;</button> <button type="button" id="panright" onclick="click_panright();" ><?php echo translate('Pan') ?> &gt;</button>
</div> </div>
<div id="timelinediv"> <div id="timelinediv">
<canvas id="timeline" onmousemove="mmove(event);" ontouchmove="tmove(event);" onmousedown="mdown(event);" onmouseup="mup(event);" onmouseout="mout(event);"></canvas> <canvas id="timeline" onmousemove="mmove(event);" ontouchmove="tmove(event);" onmousedown="mdown(event);" onmouseup="mup(event);" onmouseout="mout(event);"></canvas>
@ -270,16 +232,18 @@ input[type=range]::-ms-tooltip {
<span id="scrubright"></span> <span id="scrubright"></span>
<span id="scruboutput"></span> <span id="scruboutput"></span>
</div> </div>
<div id="monitors"> </div>
</div>
</form>
<div id="monitors">
<?php <?php
// Monitor images - these had to be loaded after the monitors used were determined (after loading events) // Monitor images - these had to be loaded after the monitors used were determined (after loading events)
foreach ($monitors as $m) { foreach ($monitors as $m) {
echo '<canvas width="' . $m->Width() * $defaultScale . 'px" height="' . $m->Height() * $defaultScale . 'px" id="Monitor' . $m->Id() . '" style="border:3px solid ' . $m->WebColour() . '" onclick="clickMonitor(event,' . $m->Id() . ')">No Canvas Support!!</canvas>'; echo '<canvas width="' . $m->Width() * $defaultScale . '" height="' . $m->Height() * $defaultScale . '" id="Monitor' . $m->Id() . '" style="border:3px solid ' . $m->WebColour() . '" onclick="clickMonitor(event,' . $m->Id() . ')">No Canvas Support!!</canvas>';
} }
?> ?>
</div> </div>
<p id="fps">evaluating fps</p> <p id="fps">evaluating fps</p>
</div> </div>
</body> <?php xhtmlFooter() ?>
</html>

View File

@ -18,9 +18,8 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// //
if ( !canView( 'System' ) ) if ( !canView( 'System' ) ) {
{ $view = 'error';
$view = "error";
return; return;
} }
@ -47,7 +46,7 @@ $tabs['users'] = translate('Users');
if ( isset($_REQUEST['tab']) ) if ( isset($_REQUEST['tab']) )
$tab = validHtmlStr($_REQUEST['tab']); $tab = validHtmlStr($_REQUEST['tab']);
else else
$tab = "system"; $tab = 'system';
$focusWindow = true; $focusWindow = true;
@ -55,7 +54,7 @@ xhtmlHeaders( __FILE__, translate('Options') );
# Have to do this stuff up here before including header.php because fof the cookie setting # Have to do this stuff up here before including header.php because fof the cookie setting
$skin_options = array_map( 'basename', glob('skins/*',GLOB_ONLYDIR) ); $skin_options = array_map( 'basename', glob('skins/*',GLOB_ONLYDIR) );
if($tab == 'skins') { if ( $tab == 'skins' ) {
$current_skin = $_COOKIE['zmSkin']; $current_skin = $_COOKIE['zmSkin'];
$reload = false; $reload = false;
if ( isset($_GET['skin-choice']) && ( $_GET['skin-choice'] != $current_skin ) ) { if ( isset($_GET['skin-choice']) && ( $_GET['skin-choice'] != $current_skin ) ) {
@ -77,12 +76,11 @@ if($tab == 'skins') {
<body> <body>
<?php echo getNavBarHTML(); ?> <?php echo getNavBarHTML(); ?>
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-sm-2 sidebar"> <div class="col-sm-2 sidebar">
<ul class="nav nav-pills nav-stacked"> <ul class="nav nav-pills nav-stacked">
<?php <?php
foreach ( $tabs as $name=>$value ) foreach ( $tabs as $name=>$value ) {
{
?> ?>
<li<?php echo $tab == $name ? ' class="active"' : '' ?>><a href="?view=<?php echo $view ?>&amp;tab=<?php echo $name ?>"><?php echo $value ?></a></li> <li<?php echo $tab == $name ? ' class="active"' : '' ?>><a href="?view=<?php echo $view ?>&amp;tab=<?php echo $name ?>"><?php echo $value ?></a></li>
<?php <?php
@ -90,51 +88,47 @@ foreach ( $tabs as $name=>$value )
?> ?>
</ul> </ul>
</div> </div>
<div class="col-sm-10 col-sm-offset-2"> <div class="col-sm-10 col-sm-offset-2">
<div id="options"> <div id="options">
<?php <?php
if($tab == 'skins') { if ( $tab == 'skins' ) {
?> ?>
<form name="optionsForm" class="form-horizontal" method="get" action="<?php echo $_SERVER['PHP_SELF'] ?>"> <form name="optionsForm" class="form-horizontal" method="get" action="<?php echo $_SERVER['PHP_SELF'] ?>">
<input type="hidden" name="view" value="<?php echo $view ?>"/> <input type="hidden" name="view" value="<?php echo $view ?>"/>
<input type="hidden" name="tab" value="<?php echo $tab ?>"/> <input type="hidden" name="tab" value="<?php echo $tab ?>"/>
<div class="form-group"> <div class="form-group">
<label for="skin-choice" class="col-sm-3 control-label">ZM_SKIN</label> <label for="skin-choice" class="col-sm-3 control-label">SKIN</label>
<div class="col-sm-6"> <div class="col-sm-6">
<select name="skin-choice" class="form-control"> <select name="skin-choice" class="form-control">
<?php <?php
foreach($skin_options as $dir) { foreach($skin_options as $dir) {
echo '<option value="'.$dir.'" '.($current_skin==$dir ? 'SELECTED="SELECTED"' : '').'>'.$dir.'</option>'; echo '<option value="'.$dir.'" '.($current_skin==$dir ? 'SELECTED="SELECTED"' : '').'>'.$dir.'</option>';
} }
?> ?>
</select> </select>
<span class="help-block"><?php echo translate('SkinDescription'); ?></span> <span class="help-block"><?php echo translate('SkinDescription'); ?></span>
</div> </div>
</div> </div>
<div class="form-group">
<div class="form-group"> <label for="css-choice" class="col-sm-3 control-label">CSS</label>
<label for="css-choice" class="col-sm-3 control-label">ZM_CSS</label>
<div class="col-sm-6"> <div class="col-sm-6">
<select name="css-choice" class="form-control"> <select name="css-choice" class="form-control">
<?php <?php
foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDIR) ) as $dir) { foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDIR) ) as $dir) {
echo '<option value="'.$dir.'" '.($current_css==$dir ? 'SELECTED="SELECTED"' : '').'>'.$dir.'</option>'; echo '<option value="'.$dir.'" '.($current_css==$dir ? 'SELECTED="SELECTED"' : '').'>'.$dir.'</option>';
} }
?> ?>
</select> </select>
<span class="help-block"><?php echo translate('CSSDescription'); ?></span> <span class="help-block"><?php echo translate('CSSDescription'); ?></span>
</div> </div>
</div> </div>
<div id="contentButtons"> <div id="contentButtons">
<input type="submit" class="btn btn-primary btn-lg" value="<?php echo translate('Save') ?>"<?php echo $canEdit?'':' disabled="disabled"' ?>/> <input type="submit" class="btn btn-primary btn-lg" value="<?php echo translate('Save') ?>"<?php echo $canEdit?'':' disabled="disabled"' ?>/>
</div> </div>
</form> </form>
<?php <?php
} } else if ( $tab == 'users' ) {
elseif ( $tab == "users" )
{
?> ?>
<form name="userForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>"> <form name="userForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>">
<input type="hidden" name="view" value="<?php echo $view ?>"/> <input type="hidden" name="view" value="<?php echo $view ?>"/>
@ -159,21 +153,17 @@ elseif ( $tab == "users" )
</thead> </thead>
<tbody> <tbody>
<?php <?php
$sql = "select * from Monitors order by Sequence asc"; $sql = 'select * from Monitors order by Sequence asc';
$monitors = array(); $monitors = array();
foreach( dbFetchAll( $sql ) as $monitor ) foreach( dbFetchAll( $sql ) as $monitor ) {
{
$monitors[$monitor['Id']] = $monitor; $monitors[$monitor['Id']] = $monitor;
} }
$sql = "select * from Users"; $sql = 'select * from Users';
foreach( dbFetchAll( $sql ) as $row ) foreach( dbFetchAll( $sql ) as $row ) {
{
$userMonitors = array(); $userMonitors = array();
if ( !empty($row['MonitorIds']) ) if ( !empty($row['MonitorIds']) ) {
{ foreach ( explode( ',', $row['MonitorIds'] ) as $monitorId ) {
foreach ( explode( ",", $row['MonitorIds'] ) as $monitorId )
{
$userMonitors[] = $monitors[$monitorId]['Name']; $userMonitors[] = $monitors[$monitorId]['Name'];
} }
} }
@ -260,7 +250,9 @@ elseif ( $tab == "users" )
</tbody> </tbody>
</table> </table>
<div id="contentButtons"> <div id="contentButtons">
<input type="button" value="<?php echo translate('AddNewStorage') ?>" onclick="createPopup( '?view=storage&amp;id=0', 'zmStorage', 'storage' );"<?php if ( !canEdit( 'System' ) ) { ?> disabled="disabled"<?php } ?>/><input type="submit" name="deleteBtn" value="<?php echo translate('Delete') ?>" disabled="disabled"/><input type="button" value="<?php echo translate('Cancel') ?>" onclick="closeWindow();"/> <input type="button" value="<?php echo translate('AddNewStorage') ?>" onclick="createPopup( '?view=storage&amp;id=0', 'zmStorage', 'storage' );"<?php if ( !canEdit( 'System' ) ) { ?> disabled="disabled"<?php } ?>/>
<input type="submit" name="deleteBtn" value="<?php echo translate('Delete') ?>" disabled="disabled"/>
<input type="button" value="<?php echo translate('Cancel') ?>" onclick="closeWindow();"/>
</div> </div>
</form> </form>
<?php <?php
@ -287,31 +279,23 @@ elseif ( $tab == "users" )
<label for="<?php echo $name ?>" class="col-sm-3 control-label"><?php echo $shortName ?></label> <label for="<?php echo $name ?>" class="col-sm-3 control-label"><?php echo $shortName ?></label>
<div class="col-sm-6"> <div class="col-sm-6">
<?php <?php
if ( $value['Type'] == "boolean" ) if ( $value['Type'] == "boolean" ) {
{
?> ?>
<input type="checkbox" id="<?php echo $name ?>" name="newConfig[<?php echo $name ?>]" value="1"<?php if ( $value['Value'] ) { ?> checked="checked"<?php } ?><?php echo $canEdit?'':' disabled="disabled"' ?>/> <input type="checkbox" id="<?php echo $name ?>" name="newConfig[<?php echo $name ?>]" value="1"<?php if ( $value['Value'] ) { ?> checked="checked"<?php } ?><?php echo $canEdit?'':' disabled="disabled"' ?>/>
<?php <?php
} } elseif ( preg_match( "/\|/", $value['Hint'] ) ) {
elseif ( preg_match( "/\|/", $value['Hint'] ) )
{
?> ?>
<?php <?php
$options = explode( '|', $value['Hint'] ); $options = explode( '|', $value['Hint'] );
if ( count( $options ) > 3 ) if ( count( $options ) > 3 ) {
{
?> ?>
<select class="form-control" name="newConfig[<?php echo $name ?>]"<?php echo $canEdit?'':' disabled="disabled"' ?>> <select class="form-control" name="newConfig[<?php echo $name ?>]"<?php echo $canEdit?'':' disabled="disabled"' ?>>
<?php <?php
foreach ( $options as $option ) foreach ( $options as $option ) {
{ if ( preg_match( '/^([^=]+)=(.+)$/', $option, $matches ) ) {
if ( preg_match( '/^([^=]+)=(.+)$/', $option, $matches ) )
{
$optionLabel = $matches[1]; $optionLabel = $matches[1];
$optionValue = $matches[2]; $optionValue = $matches[2];
} } else {
else
{
$optionLabel = $optionValue = $option; $optionLabel = $optionValue = $option;
} }
?> ?>
@ -321,18 +305,12 @@ elseif ( $tab == "users" )
?> ?>
</select> </select>
<?php <?php
} } else {
else foreach ( $options as $option ) {
{ if ( preg_match( '/^([^=]+)=(.+)$/', $option ) ) {
foreach ( $options as $option )
{
if ( preg_match( '/^([^=]+)=(.+)$/', $option ) )
{
$optionLabel = $matches[1]; $optionLabel = $matches[1];
$optionValue = $matches[2]; $optionValue = $matches[2];
} } else {
else
{
$optionLabel = $optionValue = $option; $optionLabel = $optionValue = $option;
} }
?> ?>

View File

@ -56,7 +56,7 @@ xhtmlHeaders(__FILE__, translate('Storage')." - ".$newStorage['Name'] );
</tr> </tr>
<tr> <tr>
<th scope="row"><?php echo translate('Path') ?></th> <th scope="row"><?php echo translate('Path') ?></th>
<td><input type="url" name="newStorage[Path]" value="<?php echo $newStorage['Path'] ?>"/></td> <td><input type="text" name="newStorage[Path]" value="<?php echo $newStorage['Path'] ?>"/></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@ -710,7 +710,7 @@ xhtmlHeaders(__FILE__, translate('Timeline') );
<div id="topPanel" class="graphWidth"> <div id="topPanel" class="graphWidth">
<div id="imagePanel"> <div id="imagePanel">
<div id="image" class="imageHeight"> <div id="image" class="imageHeight">
<img id="imageSrc" class="imageWidth" src="graphics/transparent.gif" alt="<?php echo translate('ViewEvent') ?>" title="<?php echo translate('ViewEvent') ?>"/> <img id="imageSrc" class="imageWidth" src="graphics/transparent.png" alt="<?php echo translate('ViewEvent') ?>" title="<?php echo translate('ViewEvent') ?>"/>
<?php <?php
if ( 0 ) { if ( 0 ) {
//due to chrome bug, has to enable https://code.google.com/p/chromium/issues/detail?id=472300 //due to chrome bug, has to enable https://code.google.com/p/chromium/issues/detail?id=472300
@ -852,7 +852,7 @@ foreach( array_keys($monEventSlots) as $monitorId ) {
foreach( array_keys($monEventSlots) as $monitorId ) { foreach( array_keys($monEventSlots) as $monitorId ) {
?> ?>
<span class="keyEntry"><?php echo $monitors[$monitorId]['Name'] ?> <span class="keyEntry"><?php echo $monitors[$monitorId]['Name'] ?>
<img id="keyBox<?php echo $monitorId ?>" class="keyBox monitorColour<?php echo $monitorId ?>" src="graphics/transparent.gif" alt="<?php echo $monitors[$monitorId]['Name'] ?>"/> <img id="keyBox<?php echo $monitorId ?>" class="keyBox monitorColour<?php echo $monitorId ?>" src="graphics/transparent.png" alt="<?php echo $monitors[$monitorId]['Name'] ?>"/>
</span> </span>
<?php <?php
} }

View File

@ -283,7 +283,9 @@ for ( $i = 0; $i < $pointCols; $i++ )
</tr> </tr>
</tbody> </tbody>
</table> </table>
<input id="pauseBtn" type="button" value="<?php echo translate('Pause') ?>" onclick="streamCmdPauseToggle()"/><input type="submit" id="submitBtn" name="submitBtn" value="<?php echo translate('Save') ?>" onclick="return saveChanges( this )"<?php if (!canEdit( 'Monitors' ) || (false && $selfIntersecting)) { ?> disabled="disabled"<?php } ?>/><input type="button" value="<?php echo translate('Cancel') ?>" onclick="refreshParentWindow(); closeWindow();"/> <input id="pauseBtn" type="button" value="<?php echo translate('Pause') ?>" onclick="streamCmdPauseToggle()"/>
<input type="submit" id="submitBtn" name="submitBtn" value="<?php echo translate('Save') ?>" onclick="return saveChanges( this )"<?php if (!canEdit( 'Monitors' ) || (false && $selfIntersecting)) { ?> disabled="disabled"<?php } ?>/>
<input type="button" value="<?php echo translate('Cancel') ?>" onclick="refreshParentWindow(); closeWindow();"/>
</div> </div>
</form> </form>
</div> </div>

View File

@ -26,10 +26,10 @@ if ( !canView( 'Monitors' ) ) {
$mid = validInt($_REQUEST['mid']); $mid = validInt($_REQUEST['mid']);
$monitor = new Monitor( $mid ); $monitor = new Monitor( $mid );
# Width() and Height() are already rotated # Width() and Height() are already rotated
$minX = 0; $minX = 0;
$maxX = $monitor->Width()-1; $maxX = $monitor->Width()-1;
$minY = 0; $minY = 0;
$maxY = $monitor->Height()-1; $maxY = $monitor->Height()-1;
$zones = array(); $zones = array();
foreach( dbFetchAll( 'SELECT * FROM Zones WHERE MonitorId=? ORDER BY Area DESC', NULL, array($mid) ) as $row ) { foreach( dbFetchAll( 'SELECT * FROM Zones WHERE MonitorId=? ORDER BY Area DESC', NULL, array($mid) ) as $row ) {
@ -71,8 +71,7 @@ xhtmlHeaders(__FILE__, translate('Zones') );
</thead> </thead>
<tbody> <tbody>
<?php <?php
foreach( $zones as $zone ) foreach( $zones as $zone ) {
{
?> ?>
<tr> <tr>
<td class="colName"><a href="#" onclick="streamCmdQuit( true ); createPopup( '?view=zone&amp;mid=<?php echo $mid ?>&amp;zid=<?php echo $zone['Id'] ?>', 'zmZone', 'zone', <?php echo $monitor->Width() ?>, <?php echo $monitor->Height() ?> ); return( false );"><?php echo $zone['Name'] ?></a></td> <td class="colName"><a href="#" onclick="streamCmdQuit( true ); createPopup( '?view=zone&amp;mid=<?php echo $mid ?>&amp;zid=<?php echo $zone['Id'] ?>', 'zmZone', 'zone', <?php echo $monitor->Width() ?>, <?php echo $monitor->Height() ?> ); return( false );"><?php echo $zone['Name'] ?></a></td>