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.")
set(ZM_CONFIG_DIR "/${CMAKE_INSTALL_SYSCONFDIR}" CACHE PATH
"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")
set(ZM_EXTRA_LIBS "" CACHE STRING
"A list of optional libraries, separated by semicolons, e.g. ssl;theora")

View File

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

View File

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

View File

@ -52,7 +52,7 @@ SET @s = (SELECT IF(
AND column_name = 'DefaultVideo'
) > 0,
"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;

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 || :
# 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
%{__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}"
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#

View File

@ -899,6 +899,21 @@ our @options = (
type => $types{integer},
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',
default => '40200',

View File

@ -348,7 +348,12 @@ Debug("Checking for files for event $_[0]{Id} at $path using glob $path/* found
sub 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)));
} else {
Warning($_[0]->Path() . ' does not appear to exist.');
}
}
return $_[0]{age};
}

View File

@ -4,7 +4,7 @@
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)
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.
add_library(zm STATIC ${ZM_BIN_SRC_FILES})

View File

@ -124,7 +124,7 @@ MYSQL_RES *zmDbRow::fetch( const char *query ) {
result_set = NULL;
Error("Error getting row from query %s. Error is %s", query, mysql_error( &dbconn ) );
} else {
Debug(3, "Succes");
Debug(5, "Success");
}
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
snprintf( id_file, sizeof(id_file), "%s/.%d", date_path, id );
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 {
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));
}
}
} // deep storage or not
// Create empty id tag file
snprintf( id_file, sizeof(id_file), "%s/.%d", path, id );
if ( FILE *id_fp = fopen( id_file, "w" ) )
fclose( id_fp );
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;
@ -262,22 +262,22 @@ void Event::createNotes( std::string &notes ) {
int Event::sd = -1;
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
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.
return( true );
if ( !config.timestamp_on_capture ) {
// 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 ) {
@ -492,10 +492,13 @@ void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *
}
}
if ( monitor->GetOptSaveJPEGs() & 1 ) {
Debug( 1, "Writing capture frame %d", frames );
WriteFrameImage( image, timestamp, event_file );
Debug( 1, "Writing capture frame %d to %s", frames, event_file );
if ( ! WriteFrameImage( image, timestamp, event_file ) ) {
Error("Failed to write frame image");
}
}
if ( videowriter != NULL ) {
Debug(3, "Writing video");
WriteFrameVideo(image, timestamp, videowriter);
}
@ -535,7 +538,7 @@ void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *
exit( mysql_errno( &dbconn ) );
}
}
}
} // end if db_frame
end_time = timestamp;

View File

@ -832,6 +832,11 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
startTime,
this->getMonitor());
} // end if record_audio
if ( ! videoStore->open() ) {
delete videoStore;
videoStore = NULL;
} else {
strcpy(oldDirectory, event_file);
monitor->SetVideoWriterEventId( last_event_id );
@ -864,6 +869,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
delete queued_packet;
} // end while packets in the packetqueue
Debug(2, "Wrote %d queued packets", packet_count );
}
} // end if ! was recording
} 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.
// 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});
}
bool Image::WriteJpeg( const char *filename) const
{
bool Image::WriteJpeg(const char *filename) const {
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);
}
bool Image::WriteJpeg( const char *filename, int quality_override, struct timeval timestamp ) const
{
if ( config.colour_jpeg_files && colours == ZM_COLOUR_GRAY8 )
{
bool Image::WriteJpeg( const char *filename, int quality_override, struct timeval timestamp ) const {
if ( config.colour_jpeg_files && colours == ZM_COLOUR_GRAY8 ) {
Image temp_image( *this );
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;
struct jpeg_compress_struct *cinfo = writejpg_ccinfo[quality];
if ( !cinfo )
{
if ( !cinfo ) {
cinfo = writejpg_ccinfo[quality] = new jpeg_compress_struct;
cinfo->err = jpeg_std_error( &jpg_err.pub );
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;
if ( (outfile = fopen( filename, "wb" )) == NULL )
{
if ( (outfile = fopen( filename, "wb" )) == NULL ) {
Error( "Can't open %s: %s", filename, strerror(errno) );
return( false );
}
@ -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");
jpeg_abort_compress( cinfo );
fclose(outfile);
return(false);
return false;
#endif
break;
}
@ -1025,7 +1018,7 @@ bool Image::WriteJpeg( const char *filename, int quality_override, struct timeva
Error("libjpeg-turbo is required for JPEG encoding directly from BGR24 source");
jpeg_abort_compress( cinfo );
fclose(outfile);
return(false);
return false;
#endif
} else {
/* 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
// 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_LEN 0x03
#define EXIFTIMES_OFFSET 0x3E // 19 characters format '2015:07:21 13:14:45' not including quotes
@ -1078,8 +1070,7 @@ cinfo->out_color_space = JCS_RGB;
JSAMPROW row_pointer; /* pointer to a single row */
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];
jpeg_write_scanlines( cinfo, &row_pointer, 1 );
}
@ -1088,7 +1079,7 @@ cinfo->out_color_space = JCS_RGB;
fclose(outfile);
return( true );
return true;
}
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
} 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

View File

@ -35,6 +35,7 @@
#endif // ZM_HAS_V4L
#include "zm_remote_camera.h"
#include "zm_remote_camera_http.h"
#include "zm_remote_camera_nvsocket.h"
#if HAVE_LIBAVFORMAT
#include "zm_remote_camera_rtsp.h"
#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++;
std::string name = dbrow[col]; 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++;
int function = atoi(dbrow[col]); col++;
int enabled = atoi(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 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
Fatal( "You must have video4linux libraries and headers installed to use local analog or USB cameras for monitor %d", id );
#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" ) {
if ( protocol == "http" ) {
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) );
}
}
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 <arpa/inet.h>
#define SOCKET_BUF_SIZE 8192
//
// Class representing 'remote' cameras, i.e. those which are
// accessed over a network connection.
//
class RemoteCamera : public Camera
{
class RemoteCamera : public Camera {
protected:
std::string protocol;
std::string host;
@ -90,6 +91,7 @@ public:
virtual int Capture( Image &image ) = 0;
virtual int PostCapture() = 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

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
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) {
Fatal("Unable to create video out stream\n");
} else {
@ -222,7 +222,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
Debug(3, "Got AAC");
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) {
Error("Unable to create audio out stream\n");
audio_out_stream = NULL;
@ -279,12 +279,25 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
}
} // 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 */
if (!(out_format->flags & AVFMT_NOFILE)) {
ret = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE, NULL, NULL);
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());
return false;
}
}
@ -310,18 +323,11 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
if (ret < 0) {
Error("Error occurred when writing out file header to %s: %s\n",
filename, av_make_error_string(ret).c_str());
return false;
}
if (opts) av_dict_free(&opts);
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
return true;
}
VideoStore::~VideoStore() {
if (audio_out_codec) {

View File

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

View File

@ -112,7 +112,7 @@ void Zone::Setup(
if ( config.record_diag_images ) {
static char diag_path[PATH_MAX] = "";
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 );
}
@ -233,7 +233,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
if ( config.record_diag_images ) {
static char diag_path[PATH_MAX] = "";
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 );
}
@ -315,7 +315,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
if ( config.record_diag_images ) {
static char diag_path[PATH_MAX] = "";
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 );
}
@ -525,7 +525,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
if ( config.record_diag_images ) {
static char diag_path[PATH_MAX] = "";
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 );
}
@ -572,7 +572,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) {
if ( config.record_diag_images ) {
static char diag_path[PATH_MAX] = "";
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 );
}
@ -981,5 +981,5 @@ void Zone::std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsig
/* Store the results */
*pixel_count = pixelsalarmed;
*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
$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 );
while ( $id = dbFetchNext( $result, 'Id' ) ) {
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 );
while ( $id = dbFetchNext( $result, 'Id' ) ) {
if ( $id == $eventId ) {
@ -389,6 +389,8 @@ function getNearEvents() {
$result = array( 'EventId'=>$eventId );
$result['PrevEventId'] = empty($prevEvent)?0:$prevEvent['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['NextEventDefVideoPath'] = empty($nextEvent)?0:(getEventDefaultVideoPath($nextEvent));
return( $result );

View File

@ -3,23 +3,19 @@
define( "MSG_TIMEOUT", ZM_WEB_AJAX_TIMEOUT );
define( "MSG_DATA_SIZE", 4+256 );
if ( !($_REQUEST['connkey'] && $_REQUEST['command']) )
{
if ( !($_REQUEST['connkey'] && $_REQUEST['command']) ) {
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()) );
}
$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()) );
}
switch ( $_REQUEST['command'] )
{
switch ( $_REQUEST['command'] ) {
case CMD_VARPLAY :
Logger::Debug( "Varplaying to ".$_REQUEST['rate'] );
$msg = pack( "lcn", MSG_CMD, $_REQUEST['command'], $_REQUEST['rate']+32768 );
@ -64,26 +60,18 @@ $wSockets = NULL;
$eSockets = NULL;
$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()) );
}
else if ( $numSockets < 0 )
{
} else if ( $numSockets < 0 ) {
ajaxError( "Socket closed $remSockFile" );
}
else if ( $numSockets == 0 )
{
} else if ( $numSockets == 0 ) {
ajaxError( "Timed out waiting for msg $remSockFile" );
}
else if ( $numSockets > 0 )
{
} else if ( $numSockets > 0 ) {
if ( count($rSockets) != 1 )
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 :
{
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 );
switch ( $data['type'] )
{
switch ( $data['type'] ) {
case MSG_DATA_WATCH :
{
$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' );
function ajaxCleanup()
{
function ajaxCleanup() {
global $socket, $locSockFile;
if ( !empty( $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;' ) {
if ( isset($this->{'ServerId'}) and $this->{'ServerId'} ) {
$Server = new Server( $this->{'ServerId'} );
$streamSrc = ZM_BASE_PROTOCOL.'://'.$Server->Hostname().ZM_PATH_ZMS;
$streamSrc = ZM_BASE_PROTOCOL.'://'.$Server->Hostname();
} 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'};

View File

@ -605,11 +605,13 @@ Warning("Addterm");
if ( canEdit( 'Groups' ) ) {
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
$monitors = empty( $_POST['newGroup']['MonitorIds'] ) ? NULL : implode(',', $_POST['newGroup']['MonitorIds']);
$monitors = empty( $_POST['newGroup']['MonitorIds'] ) ? '' : implode(',', $_POST['newGroup']['MonitorIds'] );
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 {
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 File

@ -46,11 +46,14 @@ $configSubFolder = ZM_CONFIG_SUBDIR;
if ( is_dir($configSubFolder) ) {
if ( is_readable($configSubFolder) ) {
foreach ( glob("$configSubFolder/*.conf") as $filename ) {
error_log("processing $filename");
$configvals = array_replace($configvals, process_configfile($filename) );
}
} else {
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

View File

@ -127,6 +127,10 @@ function dbQuery( $sql, $params=NULL ) {
} else {
$result = $dbConn->query( $sql );
}
//if ( $params )
//Warning("SQL: $sql" . implode(',',$params));
//else
//Warning("SQL: $sql" );
} catch(PDOException $e) {
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];
else
$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 );
}
@ -557,6 +557,9 @@ function htmlSelect( $name, $contents, $values, $behaviours=false ) {
$html = "<select name=\"$name\" id=\"$name\"$behaviourText>";
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;
$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/Storage.php' );
require_once( 'includes/Event.php' );
require_once( 'includes/Group.php' );
require_once( 'includes/Monitor.php' );
if ( isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ) {
@ -194,7 +195,7 @@ isset($view) || $view = NULL;
isset($request) || $request = 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' );
Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\"");
csrf_check();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,6 +10,9 @@
#SpeedDiv label {
margin: 0;
}
#DateTimeDiv {
display: inline-flex;
}
#scaleslideroutput,
#speedslideroutput {
@ -29,7 +32,9 @@
#monitors {
position:relative;
background-color:black;
width: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 http-equiv="X-UA-Compatible" content="IE=edge">
<title><?php echo ZM_WEB_TITLE_PREFIX ?> - <?php echo validHtmlStr($title) ?></title>
<link rel="icon" type="image/ico" href="graphics/favicon.ico"/>
<link rel="shortcut icon" href="graphics/favicon.ico"/>
<?php
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/overlay.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 ) {
?>
<script type="text/javascript" src="<?php echo $cssJsFile ?>"></script>
<?php
} else {
?>
<script type="text/javascript" src="skins/classic/js/classic.js"></script>
<?php } ?>
<script type="text/javascript" src="<?php echo $skinJsFile ?>"></script>
<script type="text/javascript" src="js/logger.js"></script>
@ -144,53 +155,24 @@ function xhtmlHeaders( $file, $title ) {
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':'';
ob_start();
global $CLANG;
global $VLANG;
global $CLANG;
global $VLANG;
global $running;
if ( $running == null )
$running = daemonCheck();
$status = $running?translate('Running'):translate('Stopped');
global $user;
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="container-fluid">
<div class="navbar-header">
@ -213,20 +195,19 @@ function getNavBarHTML() {
<?php if ( ZM_OPT_X10 && canView( 'Devices' ) ) { ?>
<li><a href="?view=devices">Devices</a></li>
<?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=filter">Filters</a></li>
<li><a href="?view=groups"<?php echo $view=='groups'?' class="selected"':''?>><?php echo translate('Groups') ?></a></li>
<li><a href="?view=filter"<?php echo $view=='filter'?' class="selected"':''?>><?php echo translate('Filters') ?></a></li>
<?php
$cycleGroup = isset($_COOKIE['zmGroup'])?$_COOKIE['zmGroup']:0;
if ( canView( 'Stream' ) && $cycleCount > 1 ) {
if ( canView( 'Stream' ) ) {
?>
<li><?php echo makePopupLink( '?view=cycle&amp;group='.$cycleGroup, 'zmCycle'.$cycleGroup, array( 'cycle', $cycleWidth, $cycleHeight ), translate('Cycle'), $running ) ?></li>
<li><?php echo makePopupLink( '?view=montage&amp;group='.$cycleGroup, 'zmMontage'.$cycleGroup, 'montage', translate('Montage'), $running ) ?></li>
<li><a href="?view=cycle"<?php echo $view=='cycle'?' class="selected"':''?>><?php echo translate('Cycle') ?></a></li>
<li><a href="?view=montage"<?php echo $view=='montage'?' class="selected"':''?>><?php echo translate('Montage') ?></a></li>
<?php
}
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
}
?>
@ -248,7 +229,7 @@ function getNavBarHTML() {
</div> <!-- End .container-fluid -->
<div class="container-fluid">
<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 class="pull-right">
<?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 ) {
if ( typeof popupSizes == 'undefined' ) {
Error( "Can't find any window sizes" );
return( { 'width': 0, 'height': 0 } );
}
var popupSize = Object.clone( popupSizes[tag] );
if ( !popupSize ) {
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 );
popupSize.height = popupSize.minHeight;
}
Debug( popupSize );
return( popupSize );
}
@ -226,6 +229,14 @@ function submitTab( tab ) {
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 ) {
var form = element.form;
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);
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 auth_hash;
<?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 } ?>

View File

@ -19,8 +19,12 @@
//
$servers = Server::find_all();
require_once('includes/Storage.php');
$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;
if ( $running == null )
$running = daemonCheck();
@ -92,33 +96,8 @@ $eventCounts = array(
),
);
$displayMonitors = NULL;
# Also populates displayMonitors
$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();
@ -137,6 +116,79 @@ xhtmlHeaders( __FILE__, translate('Console') );
<input type="hidden" name="action" value=""/>
<?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">
<table class="table table-striped table-hover table-condensed" id="consoleTable">
@ -159,7 +211,7 @@ xhtmlHeaders( __FILE__, translate('Console') );
<?php } ?>
<th class="colZones"><a href="<?php echo $_SERVER['PHP_SELF'] ?>?view=zones_overview"><?php echo translate('Zones') ?></a></th>
<?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 } ?>
</tr>
</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>';
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
}
@ -249,8 +301,8 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
<tfoot>
<tr>
<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" class="btn btn-primary" name="addBtn" value="<?php echo translate('AddNewMonitor') ?>" onclick="addMonitor( this )"/>
<input type="button" value="<?php echo translate('Refresh') ?>" onclick="location.reload(true);"/>
<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=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"/>

View File

@ -32,14 +32,39 @@ if ( empty($_REQUEST['mode']) ) {
$mode = validHtmlStr($_REQUEST['mode']);
}
$group = '';
$groupSql = '';
if ( !empty($_REQUEST['group']) ) {
$group = validInt($_REQUEST['group']);
$row = dbFetchOne( 'SELECT * FROM Groups WHERE Id = ?', NULL, array($group) );
$groupSql = " and find_in_set( Id, '".$row['MonitorIds']."' )";
$group_id = 0;
if ( isset($_REQUEST['group']) ) {
$group_id = $_REQUEST['group'];
} else if ( isset($_COOKIE['zmGroup'] ) ) {
$group_id = $_COOKIE['zmGroup'];
}
$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";
$monitors = array();
$monIdx = 0;
@ -53,6 +78,7 @@ foreach( dbFetchAll( $sql ) as $row ) {
$monitors[] = new Monitor( $row );
}
if ( $monitors ) {
$monitor = $monitors[$monIdx];
$nextMid = $monIdx==(count($monitors)-1)?$monitors[0]->Id():$monitors[$monIdx+1]->Id();
$montageWidth = $monitor->ScaledWidth();
@ -60,31 +86,51 @@ $montageHeight = $monitor->ScaledHeight();
$widthScale = ($montageWidth*SCALE_BASE)/$monitor->Width();
$heightScale = ($montageHeight*SCALE_BASE)/$monitor->Height();
$scale = (int)(($widthScale<$heightScale)?$widthScale:$heightScale);
}
noCacheHeaders();
$focusWindow = true;
xhtmlHeaders(__FILE__, translate('CycleWatch') );
?>
<body>
<div id="page">
<?php echo $navbar = getNavBarHTML(); ?>
<div id="header">
<div id="headerButtons">
<?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 { ?>
<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 } ?>
<a href="#" onclick="closeWindow(); return( false );"><?php echo translate('Close') ?></a>
</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 id="content">
<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>
</body>
</html>
<?php xhtmlFooter() ?>

View File

@ -104,7 +104,7 @@ if ( ! $Event->Id() ) {
} else {
?>
<div id="dataBar">
<table id="dataTable" class="major" cellspacing="0">
<table id="dataTable" class="major">
<tr>
<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>
@ -130,12 +130,12 @@ if ( canEdit( 'Events' ) ) {
?>
<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="archiveEvent" 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="archiveEvent"<?php echo $Event->Archived == 1 ? ' class="hidden"' : '' ?>><a href="#" onclick="archiveEvent()"><?php echo translate('Archive') ?></a></div>
<div id="unarchiveEvent"<?php echo $Event->Archived == 0 ? ' class="hidden"' : '' ?>><a href="#" onclick="unarchiveEvent()"><?php echo translate('Unarchive') ?></a></div>
<?php
} // end if can edit Events
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
} // end if Event->DefaultVideo
?>
@ -167,12 +167,23 @@ if ( $Event->DefaultVideo() ) {
var duration = <?php echo $Event->Length() ?>, startTime = '<?php echo $Event->StartTime() ?>';
addVideoTimingTrack(document.getElementById('videoobj'), LabelFormat, monitorName, duration, startTime);
nearEventsQuery( eventData.Id );
vjsReplay(<?php echo (strtotime($Event->StartTime()) + $Event->Length())*1000 ?>);
</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
} // end if DefaultVideo
?>
</div><!--eventVideo-->
<div id="imageFeed"<?php if ( $Event->DefaultVideo() ) { ?> class="hidden"<?php } ?>>
<?php if (!$Event->DefaultVideo()) { ?>
<div id="imageFeed">
<?php
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 ) );
@ -186,7 +197,7 @@ if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
}
} // 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;&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 );"/>
@ -210,6 +221,7 @@ if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
</div><!--progressBar-->
</div><!--imageFeed-->
</div>
<?php } /*end if !DefaultVideo*/ ?>
<?php
if ( $Event->SaveJPEGs() & 3 ) { // frames or analysis
?>
@ -220,7 +232,7 @@ if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
</div>
<div id="eventImagePanel">
<div id="eventImageFrame">
<img id="eventImage" src="graphics/transparent.gif" alt=""/>
<img id="eventImage" src="graphics/transparent.png" alt=""/>
<div id="eventImageBar">
<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>

View File

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

View File

@ -18,56 +18,98 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
if ( !canEdit( 'Groups' ) )
{
$view = "error";
if ( !canEdit( 'Groups' ) ) {
$view = 'error';
return;
}
if ( !empty($_REQUEST['gid']) )
{
$newGroup = dbFetchGroup( $_REQUEST['gid'] );
}
else
{
$newGroup = array(
"Id" => "",
"Name" => "New Group",
"MonitorIds" => ""
);
if ( !empty($_REQUEST['gid']) ) {
$newGroup = new Group( $_REQUEST['gid'] );
} else {
$newGroup = new Group();
}
xhtmlHeaders( __FILE__, translate('Group')." - ".$newGroup['Name'] );
xhtmlHeaders( __FILE__, translate('Group').' - '.$newGroup->Name() );
?>
<body>
<div id="page">
<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 id="content">
<form name="groupForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>">
<input type="hidden" name="view" value="<?php echo $view ?>"/>
<input type="hidden" name="action" value="group"/>
<input type="hidden" name="gid" value="<?php echo $newGroup['Id'] ?>"/>
<table id="contentTable" class="major" cellspacing="0">
<input type="hidden" name="gid" value="<?php echo $newGroup->Id() ?>"/>
<table id="contentTable" class="major">
<tbody>
<tr>
<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>
<th scope="row"><?php echo translate('MonitorIds') ?></th>
<th scope="row"><?php echo translate('ParentGroup') ?></th>
<td>
<select name="newGroup[MonitorIds][]" size="4" multiple="multiple">
<?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'] ) )
{
$Groups = array();
foreach ( Group::find_all( ) as $Group ) {
$Groups[$Group->Id()] = $Group;
}
# This array is indexed by parent_id
$children = array();
foreach ( $Groups as $id=>$Group ) {
if ( $Group->ParentId() != null ) {
if ( ! isset( $children[$Group->ParentId()] ) )
$children[$Group->ParentId()] = array();
$children[$Group->ParentId()][] = $Group;
}
}
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
}
}
@ -78,7 +120,7 @@ xhtmlHeaders( __FILE__, translate('Group')." - ".$newGroup['Name'] );
</tbody>
</table>
<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()"/>
</div>
</form>

View File

@ -19,67 +19,80 @@
//
if ( !canView( 'Groups' ) ) {
$view = "error";
$view = 'error';
return;
}
$sql = "select * from Groups order by Name";
$groups = array();
$selected = false;
foreach( dbFetchAll( $sql ) as $row )
{
if ( !empty($_COOKIE['zmGroup']) && ($row['Id'] == $_COOKIE['zmGroup']) )
{
$row['selected'] = true;
$selected = true;
}
else
{
$row['selected'] = false;
}
$groups[] = $row;
# This will end up with the group_id of the deepest selection
$group_id = 0;
$max_depth = 0;
$Groups = array();
foreach ( Group::find_all( ) as $Group ) {
$Groups[$Group->Id()] = $Group;
}
# 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') );
?>
<body>
<div id="page">
<div id="header">
<h2><?php echo translate('Groups') ?></h2>
</div>
<?php echo $navbar = getNavBarHTML(); ?>
<div id="content">
<form name="groupsForm" method="get" action="<?php echo $_SERVER['PHP_SELF'] ?>">
<input type="hidden" name="view" value="none"/>
<input type="hidden" name="action" value="setgroup"/>
<table id="contentTable" class="major" cellspacing="0">
<table id="contentTable" class="major">
<thead>
<tr>
<th class="colName"><?php echo translate('Name') ?></th>
<th class="colIds"><?php echo translate('MonitorIds') ?></th>
<th class="colSelect"><?php echo translate('Select') ?></th>
<th class="colName" colspan="<?php echo $max_depth ?>"><?php echo translate('Name') ?></th>
<th class="colIds"><?php echo translate('Monitors') ?></th>
<th class="colSelect"><?php echo translate('Mark') ?></th>
</tr>
</thead>
<tbody>
<tr class="highlight">
<td class="colName"><?php echo translate('NoGroup') ?></td>
<td class="colIds"><?php echo translate('All') ?></td>
<td class="colSelect"><input type="radio" name="gid" value="0"<?php echo !$selected?' checked="checked"':'' ?> onclick="configureButtons( this );"/></td>
<?php
function group_line( $Group ) {
global $children;
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>
<?php foreach ( $groups as $group ) { ?>
<tr>
<td class="colName"><?php echo validHtmlStr($group['Name']) ?></td>
<td class="colIds"><?php echo monitorIdsToNames( $group['MonitorIds'], 30 ) ?></td>
<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 } ?>
';
if ( isset( $children[$Group->Id()] ) ) {
foreach ( $children[$Group->Id()] as $G ) {
$html .= group_line( $G );
}
}
return $html;
}
foreach ( $children[null] as $Group )
echo group_line( $Group );
?>
</tbody>
</table>
<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" 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();"/>
<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"/>
</div>
</form>
</div>

View File

@ -1,5 +1,5 @@
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() {

View File

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

View File

@ -1,5 +1,36 @@
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 ) {
if ( element ) {
element.className = butClass;
@ -164,14 +195,24 @@ function streamFastRev( 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 );
}
}
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 );
}
}
function streamZoomIn( x, y ) {
streamReq.send( streamParms+"&command="+CMD_ZOOMIN+"&x="+x+"&y="+y );
@ -251,6 +292,8 @@ function eventQuery( eventId ) {
var prevEventId = 0;
var nextEventId = 0;
var prevEventStartTime = 0;
var nextEventStartTime = 0;
var PrevEventDefVideoPath = "";
var NextEventDefVideoPath = "";
@ -259,6 +302,8 @@ function getNearEventsResponse( respObj, respText ) {
return;
prevEventId = respObj.nearevents.PrevEventId;
nextEventId = respObj.nearevents.NextEventId;
prevEventStartTime = Date.parse(respObj.nearevents.PrevEventStartTime);
nextEventStartTime = Date.parse(respObj.nearevents.NextEventStartTime);
PrevEventDefVideoPath = respObj.nearevents.PrevEventDefVideoPath;
NextEventDefVideoPath = respObj.nearevents.NextEventDefVideoPath;
@ -266,12 +311,14 @@ function getNearEventsResponse( respObj, respText ) {
if ( prevEventBtn ) prevEventBtn.disabled = !prevEventId;
var nextEventBtn = $('nextEventBtn');
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 } );
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 );
}
@ -455,7 +502,7 @@ function checkFrames( eventId, frameId, loadImage ) {
for ( var fid = loFid; fid <= hiFid; fid++ ) {
if ( !$('eventThumb'+fid) ) {
var img = new Element( 'img', { 'id': 'eventThumb'+fid, 'src': 'graphics/transparent.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 ); } );
frameQuery( eventId, fid, loadImage && (fid == frameId) );
var imgs = $('eventThumbs').getElements( 'img' );

View File

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

View File

@ -9,7 +9,12 @@ if ( refreshParent ) {
function configureButtons( element ) {
if ( canEditGroups ) {
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' );
}
function editGroup( element ) {
function setGroup( element ) {
var form = element.form;
form.action.value = 'setgroup';
form.submit();
}
function editGroup( element ) {
var form = element.form;
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 editGroup( gid ) {
createPopup( '?view=group&gid='+gid, 'zmGroup', 'group' );
}
function deleteGroup( element ) {
@ -29,7 +23,6 @@ function configureButtons( element ) {
if ( canEditGroups ) {
var form = element.form;
if ( element.checked ) {
form.editBtn.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(/rand=\d+/i,'rand='+Math.floor((Math.random() * 1000000) ));
streamImg.src = src;
}
streamImg.style.width = width? width + "px" : null;
streamImg.style.height = height ? height + "px" : null;
@ -210,7 +209,6 @@ function changeScale() {
Cookie.write( 'zmMontageHeight', '', { duration: 10*365 } );
}
var monitors = new Array();
function initPage() {
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(typeof underSlider !== 'undefined')
{
if(typeof underSlider !== 'undefined') {
ctx.putImageData(underSlider,underSliderX, 0, 0, 0, sliderWidth, sliderHeight);
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
underSlider=ctx.getImageData(sliderX, 0, sliderWidth, sliderHeight);
// And add in the slider'
@ -183,13 +182,10 @@ function drawSliderOnGraph(val) {
underSliderX=sliderX;
}
var o = $('scruboutput');
if(liveMode==1)
{
if(liveMode==1) {
o.innerHTML="Live Feed @ " + (1000 / currentDisplayInterval).toFixed(1) + " fps";
o.style.color="red";
}
else
{
} else {
o.innerHTML=secs2dbstr(val);
o.style.color="blue";
}
@ -293,16 +289,10 @@ function drawGraph()
return;
}
function redrawScreen()
{
if(fitMode==0) // 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");
}
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
{
function redrawScreen() {
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
$('DateTimeDiv').style.display="none";
$('SpeedDiv').style.display="none";
$('timelinediv').style.display="none";
$('live').innerHTML="History";
@ -311,9 +301,9 @@ function redrawScreen()
$('panleft').style.display="none";
$('panright').style.display="none";
}
else // switch out of liveview mode
{
} else {
// switch out of liveview mode
$('DateTimeDiv').style.display="inline";
$('SpeedDiv').style.display="inline";
$('SpeedDiv').style.display="inline-flex";
$('timelinediv').style.display=null;
@ -328,8 +318,7 @@ function redrawScreen()
$('panright').style.display="inline-flex";
}
if(fitMode==1)
{
if ( fitMode == 1 ) {
$('ScaleDiv').style.display="none";
$('fit').innerHTML="Scale";
var vh=window.innerHeight;
@ -339,9 +328,12 @@ function redrawScreen()
$('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
fitMode=1-fitMode;
}
else // switch out of fit mode
{
} else {
// 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-flex";
$('fit').innerHTML="Fit";
@ -353,11 +345,9 @@ function redrawScreen()
}
function outputUpdate(val)
{
function outputUpdate(val) {
drawSliderOnGraph(val);
for(var i=0; i<numMonitors; i++)
{
for(var i=0; i<numMonitors; i++) {
loadImage2Monitor(monitorPtr[i],SetImageSource(monitorPtr[i],val));
}
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");
return st;
}
function setFit(value)
{
function setFit(value) {
fitMode=value;
redrawScreen();
}
@ -431,13 +423,12 @@ function setScale(newscale) // makes actual change
currentScale=newscale;
}
function showSpeed(val) // updates slider only
{
function showSpeed(val) {
// updates slider only
$('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;
if(liveMode==1) return; // we shouldn't actually get here but just in case
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
}
function setLive(value)
{
function setLive(value) {
liveMode=value;
redrawScreen();
}
@ -465,13 +455,16 @@ function clicknav(minSecs,maxSecs,arch,live) {// we use the current time if we c
if ( minSecs > 0 ) {
if(maxSecs > 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 ) {
minStr="&minTime=01/01/1950 12:00:00";
maxStr="&maxTime=12/31/2035 12:00:00";
minStr="&minTime=01/01/1950T12:00:00";
maxStr="&maxTime=12/31/2035T12:00:00";
}
var intervalStr="&displayinterval=" + currentDisplayInterval.toString();
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
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;
}
} // end function clickNav
function lastHour() {
function click_lastHour() {
var now = new Date() / 1000;
clicknav(now - 3600 + 1, now,1,0);
}
function lastEight() {
function click_lastEight() {
var now = new Date() / 1000;
clicknav(now - 3600*8 + 1, now,1,0);
}
function zoomin() {
function click_zoomin() {
rangeTimeSecs = parseInt(rangeTimeSecs / 2);
minTimeSecs = parseInt(currentTimeSecs - rangeTimeSecs/2); // this is the slider current time, we center on that
maxTimeSecs = parseInt(currentTimeSecs + rangeTimeSecs/2);
clicknav(minTimeSecs,maxTimeSecs,1,0);
}
function zoomout() {
function click_zoomout() {
rangeTimeSecs = parseInt(rangeTimeSecs * 2);
minTimeSecs = parseInt(currentTimeSecs - rangeTimeSecs/2); // this is the slider current time, we center on that
maxTimeSecs = parseInt(currentTimeSecs + rangeTimeSecs/2);
clicknav(minTimeSecs,maxTimeSecs,1,0);
}
function panleft() {
function click_panleft() {
minTimeSecs = parseInt(minTimeSecs - rangeTimeSecs/2);
maxTimeSecs = minTimeSecs + rangeTimeSecs - 1;
clicknav(minTimeSecs,maxTimeSecs,1,0);
}
function panright() {
function click_panright() {
minTimeSecs = parseInt(minTimeSecs + rangeTimeSecs/2);
maxTimeSecs = minTimeSecs + rangeTimeSecs - 1;
clicknav(minTimeSecs,maxTimeSecs,1,0);
}
function allof() {
function click_all_events() {
clicknav(0,0,1,0);
}
function allnon() {
@ -690,6 +683,10 @@ function clickMonitor(event,monId) {
return;
}
function changeDateTime(e) {
e.form.submit();
}
// >>>>>>>>> Initialization that runs on window load by being at the bottom
function initPage() {

View File

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

View File

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

View File

@ -94,7 +94,7 @@ function previewEvent( eventId, frameId ) {
if ( event['frames'] ) {
if ( event['frames'][frameId] ) {
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;
loadEventImage( imagePath, eventId, frameId, event.Width, event.Height, event.Frames/event.Length, videoName, event.Length, event.StartTime, monitors[event.MonitorId]);
return;

View File

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

View File

@ -51,11 +51,13 @@ if ( ! $Server ) {
$Server = array( 'Id' => '' );
}
$monitor = null;
if ( ! empty($_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']) );
} else {
}
if ( ! $monitor ) {
$nextId = getTableAutoInc( 'Monitors' );
if ( isset( $_REQUEST['dupId'] ) ) {
@ -207,7 +209,8 @@ $sourceTypes = array(
'File' => translate('File'),
'Ffmpeg' => translate('Ffmpeg'),
'Libvlc' => translate('Libvlc'),
'cURL' => 'cURL (HTTP(S) only)'
'cURL' => 'cURL (HTTP(S) only)',
'NVSocket' => translate('NVSocket')
);
if ( !ZM_HAS_V4L )
unset($sourceTypes['Local']);
@ -395,12 +398,12 @@ $Colours = array(
);
$orientations = array(
translate('Normal') => '0',
translate('RotateRight') => '90',
translate('Inverted') => '180',
translate('RotateLeft') => '270',
translate('FlippedHori') => 'hori',
translate('FlippedVert') => 'vert'
'0' => translate('Normal'),
'90' => translate('RotateRight'),
'180' => translate('Inverted'),
'270' => translate('RotateLeft'),
'horz' => translate('FlippedHori'),
'vert' => translate('FlippedVert')
);
$deinterlaceopts = array(
@ -498,7 +501,7 @@ if ( canEdit( 'Monitors' ) ) {
<?php
} // 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 id="content">
<ul class="tabList">
@ -702,19 +705,19 @@ switch ( $tab ) {
}
?>
</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>
<td><?php echo translate('LinkedMonitors') ?></td>
<td>
<select name="monitorIds" size="4" multiple="multiple" onchange="updateLinkedMonitors( this )">
<?php
$monitors = dbFetchAll( 'select Id,Name from Monitors order by Sequence asc' );
if ( !empty($monitor->LinkedMonitors()) )
if ( $monitor->LinkedMonitors() )
$monitorIds = array_flip( explode( ',', $monitor->LinkedMonitors()) );
else
$monitorIds = array();
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>
<?php
@ -726,7 +729,7 @@ switch ( $tab ) {
</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
if ( $monitor->Type() != 'Local' && $monitor->Type() != 'File' ) {
if ( $monitor->Type() != 'Local' && $monitor->Type() != 'File' && $monitor->Type() != 'NVSocket' ) {
?>
<tr>
<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>
<tr><td><?php echo translate('V4LCapturesPerFrame') ?></td><td><input type="number" name="newMonitor[V4LCapturesPerFrame]" value="<?php echo $monitor->V4LCapturesPerFrame()?>"/></td></tr>
<?php
} 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>
<?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>
<?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('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>
<?php
} else if ( $monitor->Type() == 'File' ) {
@ -857,13 +863,15 @@ 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>
<?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('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('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
}
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>
@ -877,7 +885,7 @@ switch ( $tab ) {
<?php
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
}
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('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('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
break;
case 'timestamp' :
@ -915,12 +923,12 @@ switch ( $tab ) {
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('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('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
$return_options = array(
'-1' => translate('None'),
@ -987,7 +995,7 @@ switch ( $tab ) {
</tr>
<tr>
<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>
<?php
break;
@ -1000,7 +1008,6 @@ switch ( $tab ) {
<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()"/>
</div>
</form>
</div>
</div>

View File

@ -23,16 +23,6 @@ if ( !canView( 'Stream' ) ) {
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;
$showZones = false;
if ( isset( $_REQUEST['showZones'] ) ) {
@ -40,7 +30,6 @@ if ( isset( $_REQUEST['showZones'] ) ) {
$showZones = true;
}
}
$monitors = array();
$widths = array(
'' => 'auto',
160 => 160,
@ -68,26 +57,6 @@ if ( isset( $_REQUEST['scale'] ) ) {
if ( ! $scale )
$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;
$layouts = array(
@ -114,10 +83,42 @@ else
if ( $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') );
?>
<body>
<div id="page">
<?php echo getNavBarHTML() ?>
<div id="header">
<div id="headerButtons">
<?php
@ -136,10 +137,13 @@ if ( $showZones ) {
<?php
}
?>
<a href="#" onclick="closeWindow()"><?php echo translate('Close') ?></a>
</div>
<h2><?php echo translate('Montage') ?></h2>
<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="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>
@ -224,5 +228,4 @@ foreach ( $monitors as $monitor ) {
</div>
</div>
</div>
</body>
</html>
<?php xhtmlFooter() ?>

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
// 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' ) ) {
$view = 'error';
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.
if ( !empty($_REQUEST['group']) ) {
$group = $_REQUEST['group'];
$row = dbFetchOne( 'SELECT * FROM Groups WHERE Id = ?', NULL, array($_REQUEST['group']) );
$monitorsSql = "SELECT * FROM Monitors WHERE Function != 'None' AND find_in_set( Id, '".$row['MonitorIds']."' ) ";
} else {
$monitorsSql = "SELECT * FROM Monitors WHERE Function != 'None'";
$group = '';
$groupSql = Group::get_group_sql( $group_id );
session_start();
foreach ( array('minTime','maxTime') as $var ) {
if ( isset( $_REQUEST[$var] ) ) {
$_SESSION[$var] = $_REQUEST[$var];
}
}
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
// 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']) ) {
$maxTime = strftime("%c",time());
$minTime = strftime("%c",time() - 3600);
$time = time();
$maxTime = strftime("%FT%T",$time);
$minTime = strftime("%FT%T",$time - 3600);
}
if ( isset($_REQUEST['minTime']) )
$minTime = validHtmlStr($_REQUEST['minTime']);
@ -197,7 +159,6 @@ for ( $i = 0; $i < count($speeds); $i++ ) {
if ( isset($_REQUEST['current']) )
$defaultCurrentTime = validHtmlStr($_REQUEST['current']);
$initialModeIsLive = 1;
if ( isset($_REQUEST['live']) && $_REQUEST['live']=='0' )
$initialModeIsLive=0;
@ -230,18 +191,19 @@ foreach( dbFetchAll( $monitorsSql ) as $row ) {
xhtmlHeaders(__FILE__, translate('MontageReview') );
?>
<style>
input[type=range]::-ms-tooltip {
display: none;
}
</style>
<body>
<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="headerButtons">
<a href="#" onclick="closeWindow();"><?php echo translate('Close') ?></a>
</div>
<h2><?php echo translate('MontageReview') ?></h2>
<div id="headerControl">
<span id="groupControl"><label><?php echo translate('Group') ?>:</label>
<?php echo $group_dropdowns; ?>
</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 id="ScaleDiv">
<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>
</div>
<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="zoomin" onclick="zoomin();" ><?php echo translate('In +') ?></button>
<button type="button" id="zoomout" onclick="zoomout();" ><?php echo translate('Out -') ?></button>
<button type="button" id="lasteight" onclick="lastEight();" ><?php echo translate('8 Hour') ?></button>
<button type="button" id="lasthour" onclick="lastHour();" ><?php echo translate('1 Hour') ?></button>
<button type="button" id="allof" onclick="allof();" ><?php echo translate('All Events') ?></button>
<button type="button" id="panleft" onclick="click_panleft();" >&lt; <?php echo translate('Pan') ?></button>
<button type="button" id="zoomin" onclick="click_zoomin();" ><?php echo translate('In +') ?></button>
<button type="button" id="zoomout" onclick="click_zoomout();" ><?php echo translate('Out -') ?></button>
<button type="button" id="lasteight" onclick="click_lastEight();" ><?php echo translate('8 Hour') ?></button>
<button type="button" id="lasthour" onclick="click_lastHour();" ><?php echo translate('1 Hour') ?></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="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 id="timelinediv">
<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="scruboutput"></span>
</div>
</div>
</div>
</form>
<div id="monitors">
<?php
// Monitor images - these had to be loaded after the monitors used were determined (after loading events)
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>
<p id="fps">evaluating fps</p>
</div>
</body>
</html>
<?php xhtmlFooter() ?>

View File

@ -18,9 +18,8 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
if ( !canView( 'System' ) )
{
$view = "error";
if ( !canView( 'System' ) ) {
$view = 'error';
return;
}
@ -47,7 +46,7 @@ $tabs['users'] = translate('Users');
if ( isset($_REQUEST['tab']) )
$tab = validHtmlStr($_REQUEST['tab']);
else
$tab = "system";
$tab = 'system';
$focusWindow = true;
@ -81,8 +80,7 @@ if($tab == 'skins') {
<div class="col-sm-2 sidebar">
<ul class="nav nav-pills nav-stacked">
<?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>
<?php
@ -90,7 +88,6 @@ foreach ( $tabs as $name=>$value )
?>
</ul>
</div>
<div class="col-sm-10 col-sm-offset-2">
<div id="options">
<?php
@ -100,7 +97,7 @@ if($tab == 'skins') {
<input type="hidden" name="view" value="<?php echo $view ?>"/>
<input type="hidden" name="tab" value="<?php echo $tab ?>"/>
<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">
<select name="skin-choice" class="form-control">
<?php
@ -112,9 +109,8 @@ if($tab == 'skins') {
<span class="help-block"><?php echo translate('SkinDescription'); ?></span>
</div>
</div>
<div class="form-group">
<label for="css-choice" class="col-sm-3 control-label">ZM_CSS</label>
<label for="css-choice" class="col-sm-3 control-label">CSS</label>
<div class="col-sm-6">
<select name="css-choice" class="form-control">
<?php
@ -132,9 +128,7 @@ if($tab == 'skins') {
</form>
<?php
}
elseif ( $tab == "users" )
{
} else if ( $tab == 'users' ) {
?>
<form name="userForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>">
<input type="hidden" name="view" value="<?php echo $view ?>"/>
@ -159,21 +153,17 @@ elseif ( $tab == "users" )
</thead>
<tbody>
<?php
$sql = "select * from Monitors order by Sequence asc";
$sql = 'select * from Monitors order by Sequence asc';
$monitors = array();
foreach( dbFetchAll( $sql ) as $monitor )
{
foreach( dbFetchAll( $sql ) as $monitor ) {
$monitors[$monitor['Id']] = $monitor;
}
$sql = "select * from Users";
foreach( dbFetchAll( $sql ) as $row )
{
$sql = 'select * from Users';
foreach( dbFetchAll( $sql ) as $row ) {
$userMonitors = array();
if ( !empty($row['MonitorIds']) )
{
foreach ( explode( ",", $row['MonitorIds'] ) as $monitorId )
{
if ( !empty($row['MonitorIds']) ) {
foreach ( explode( ',', $row['MonitorIds'] ) as $monitorId ) {
$userMonitors[] = $monitors[$monitorId]['Name'];
}
}
@ -260,7 +250,9 @@ elseif ( $tab == "users" )
</tbody>
</table>
<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>
</form>
<?php
@ -287,31 +279,23 @@ elseif ( $tab == "users" )
<label for="<?php echo $name ?>" class="col-sm-3 control-label"><?php echo $shortName ?></label>
<div class="col-sm-6">
<?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"' ?>/>
<?php
}
elseif ( preg_match( "/\|/", $value['Hint'] ) )
{
} elseif ( preg_match( "/\|/", $value['Hint'] ) ) {
?>
<?php
$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"' ?>>
<?php
foreach ( $options as $option )
{
if ( preg_match( '/^([^=]+)=(.+)$/', $option, $matches ) )
{
foreach ( $options as $option ) {
if ( preg_match( '/^([^=]+)=(.+)$/', $option, $matches ) ) {
$optionLabel = $matches[1];
$optionValue = $matches[2];
}
else
{
} else {
$optionLabel = $optionValue = $option;
}
?>
@ -321,18 +305,12 @@ elseif ( $tab == "users" )
?>
</select>
<?php
}
else
{
foreach ( $options as $option )
{
if ( preg_match( '/^([^=]+)=(.+)$/', $option ) )
{
} else {
foreach ( $options as $option ) {
if ( preg_match( '/^([^=]+)=(.+)$/', $option ) ) {
$optionLabel = $matches[1];
$optionValue = $matches[2];
}
else
{
} else {
$optionLabel = $optionValue = $option;
}
?>

View File

@ -56,7 +56,7 @@ xhtmlHeaders(__FILE__, translate('Storage')." - ".$newStorage['Name'] );
</tr>
<tr>
<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>
</tbody>
</table>

View File

@ -710,7 +710,7 @@ xhtmlHeaders(__FILE__, translate('Timeline') );
<div id="topPanel" class="graphWidth">
<div id="imagePanel">
<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
if ( 0 ) {
//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 ) {
?>
<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>
<?php
}

View File

@ -283,7 +283,9 @@ for ( $i = 0; $i < $pointCols; $i++ )
</tr>
</tbody>
</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>
</form>
</div>

View File

@ -71,8 +71,7 @@ xhtmlHeaders(__FILE__, translate('Zones') );
</thead>
<tbody>
<?php
foreach( $zones as $zone )
{
foreach( $zones as $zone ) {
?>
<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>