Merge branch 'master' of github.com:ZoneMinder/zoneminder

This commit is contained in:
Isaac Connor 2020-12-15 16:50:09 -05:00
commit b7cc5dde54
92 changed files with 1290 additions and 20087 deletions

View File

@ -148,6 +148,9 @@ set(ZM_CACHEDIR "/var/cache/zoneminder" CACHE PATH
"Location of the web server cache busting files, default: /var/cache/zoneminder")
set(ZM_CONTENTDIR "/var/lib/zoneminder" CACHE PATH
"Location of dynamic content (events and images), default: /var/lib/zoneminder")
set(ZM_FONTDIR "${CMAKE_INSTALL_FULL_DATADIR}/zoneminder/fonts" CACHE PATH
"Location of the font files used for timestamping, default: <prefix>/${CMAKE_INSTALL_DATADIR}/zoneminder/fonts")
set(ZM_DB_HOST "localhost" CACHE STRING
"Hostname where ZoneMinder database located, default: localhost")
set(ZM_DB_NAME "zm" CACHE STRING
@ -911,6 +914,7 @@ set(BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS_SAVED}")
add_subdirectory(src)
add_subdirectory(scripts)
add_subdirectory(db)
add_subdirectory(fonts)
add_subdirectory(web)
add_subdirectory(misc)
add_subdirectory(onvif)

View File

@ -337,6 +337,7 @@ CREATE TABLE `Groups` (
`Id` int(10) unsigned NOT NULL auto_increment,
`Name` varchar(64) NOT NULL default '',
`ParentId` int(10) unsigned,
FOREIGN KEY (`ParentId`) REFERENCES `Groups` (`Id`) ON DELETE CASCADE,
PRIMARY KEY (`Id`)
) ENGINE=@ZM_MYSQL_ENGINE@;
@ -348,7 +349,9 @@ DROP TABLE IF EXISTS `Groups_Monitors`;
CREATE TABLE `Groups_Monitors` (
`Id` INT(10) unsigned NOT NULL auto_increment,
`GroupId` int(10) unsigned NOT NULL,
FOREIGN KEY (`GroupId`) REFERENCES `Groups` (`Id`) ON DELETE CASCADE,
`MonitorId` int(10) unsigned NOT NULL,
FOREIGN KEY (`MonitorId`) REFERENCES `Monitors` (`Id`) ON DELETE CASCADE,
PRIMARY KEY (`Id`)
) ENGINE=@ZM_MYSQL_ENGINE@;
@ -447,6 +450,7 @@ CREATE TABLE `Monitors` (
`Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local',
`Function` enum('None','Monitor','Modect','Record','Mocord','Nodect') NOT NULL default 'Monitor',
`Enabled` tinyint(3) unsigned NOT NULL default '1',
`DecodingEnabled` tinyint(3) unsigned NOT NULL default '1',
`LinkedMonitors` varchar(255),
`Triggers` set('X10') NOT NULL default '',
`ONVIF_URL` VARCHAR(255) NOT NULL DEFAULT '',

70
db/zm_update-1.35.15.sql Normal file
View File

@ -0,0 +1,70 @@
/*
Add the EndTime IS NOT NULL term to the Update Disk Space Filter.
This will only work if they havn't modified the stock filter
*/
UPDATE Filters SET Query_json='{"terms":[{"attr":"DiskSpace","op":"IS","val":"NULL"},{"cnj":"and","obr":"0","attr":"EndDateTime","op":"IS NOT","val":"NULL","cbr":"0"}]}' WHERE Query_json='{"terms":[{"attr":"DiskSpace","op":"IS","val":"NULL"}]}';
/*
Add the EndTime IS NOT NULL term to the Purge When Full Filter.
This will only work if they havn't modified the stock filter .
This is important to prevent SQL Errors inserting into Frames table if PurgeWhenFull deletes in-progress events.
*/
UPDATE Filters SET Query_json='{"sort_field":"Id","terms":[{"val":0,"attr":"Archived","op":"="},{"cnj":"and","val":95,"attr":"DiskPercent","op":">="},{"cnj":"and","obr":"0","attr":"EndDateTime","op":"IS NOT","val":"NULL","cbr":"0"}],"limit":100,"sort_asc":1}' WHERE Query_json='{"sort_field":"Id","terms":[{"val":0,"attr":"Archived","op":"="},{"cnj":"and","val":95,"attr":"DiskPercent","op":">="}],"limit":100,"sort_asc":1}';
/* Add FOREIGN KEYS After deleting lost records */
set @exist := (select count(*) FROM information_schema.key_column_usage where table_name='Groups_Monitors' and column_name='GroupId' and referenced_table_name='Groups' and referenced_column_name='Id');
set @sqlstmt := if( @exist > 1, "SELECT 'You have more than 1 FOREIGN KEY. Please do manual cleanup'", "SELECT 'Ok'");
set @sqlstmt := if( @exist = 1, "SELECT 'FOREIGN KEY GroupId in Groups_Monitors already exists'", @sqlstmt);
set @sqlstmt := if( @exist = 0, "SELECT 'Adding foreign key for GroupId to Groups_Monitors'", @sqlstmt);
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @sqlstmt := if( @exist = 0, "SELECT 'Deleting unlinked Groups_Monitors'", "SELECT '.'");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @sqlstmt := if( @exist = 0, "DELETE FROM `Groups_Monitors` WHERE `GroupId` NOT IN (SELECT `Id` FROM `Groups`)", "SELECT '.'");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @sqlstmt := if( @exist = 0, "ALTER TABLE `Groups_Monitors` ADD FOREIGN KEY (`GroupId`) REFERENCES `Groups` (`Id`) ON DELETE CASCADE", "SELECT '.'");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
/* Add FOREIGN KEYS After deleting lost records */
set @exist := (select count(*) FROM information_schema.key_column_usage where table_name='Groups_Monitors' and column_name='MonitorId' and referenced_table_name='Monitors' and referenced_column_name='Id');
set @sqlstmt := if( @exist > 1, "SELECT 'You have more than 1 FOREIGN KEY. Please do manual cleanup'", "SELECT 'Ok'");
set @sqlstmt := if( @exist = 1, "SELECT 'FOREIGN KEY MonitorId in Groups_Monitors already exists'", @sqlstmt);
set @sqlstmt := if( @exist = 0, "SELECT 'Adding foreign key for MonitorId to Groups_Monitors'", @sqlstmt);
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @sqlstmt := if( @exist = 0, "SELECT 'Deleting unlinked Groups_Monitors'", "SELECT '.'");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @sqlstmt := if( @exist = 0, "DELETE FROM `Groups_Monitors` WHERE `MonitorId` NOT IN (SELECT `Id` FROM `Monitors`)", "SELECT '.'");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @sqlstmt := if( @exist = 0, "ALTER TABLE `Groups_Monitors` ADD FOREIGN KEY (`MonitorId`) REFERENCES `Monitors` (`Id`) ON DELETE CASCADE", "SELECT '.'");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
/* Add FOREIGN KEYS After deleting lost records */
set @exist := (select count(*) FROM information_schema.key_column_usage where table_name='Groups' and column_name='ParentId' and referenced_table_name='Groups' and referenced_column_name='Id');
set @sqlstmt := if( @exist > 1, "SELECT 'You have more than 1 FOREIGN KEY. Please do manual cleanup'", "SELECT 'Ok'");
set @sqlstmt := if( @exist = 1, "SELECT 'FOREIGN KEY ParentId in Groups already exists'", @sqlstmt);
set @sqlstmt := if( @exist = 0, "SELECT 'Adding foreign key for ParentId to Groups'", @sqlstmt);
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @sqlstmt := if( @exist = 0, "SELECT 'Deleting unlinked Groups'", "SELECT '.'");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @sqlstmt := if( @exist = 0, "UPDATE `Groups` SET `ParentId` = NULL WHERE (ParentId IS NOT NULL) and ParentId NOT IN (SELECT * FROM(SELECT Id FROM `Groups`)tblTmp)", "SELECT '.'");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @sqlstmt := if( @exist = 0, "ALTER TABLE `Groups` ADD FOREIGN KEY (ParentId) REFERENCES `Groups` (Id) ON DELETE CASCADE", "SELECT '.'");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;

12
db/zm_update-1.35.16.sql Normal file
View File

@ -0,0 +1,12 @@
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Monitors'
AND column_name = 'DecodingEnabled'
) > 0,
"SELECT 'Column DecodingEnabled already exists in Monitors'",
"ALTER TABLE Monitors ADD `DecodingEnabled` tinyint(3) unsigned NOT NULL default '1' AFTER `Enabled`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

View File

@ -28,7 +28,7 @@
%global _hardened_build 1
Name: zoneminder
Version: 1.35.14
Version: 1.35.16
Release: 1%{?dist}
Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons

View File

@ -5,5 +5,6 @@ var/cache/zoneminder/images
var/cache/zoneminder/temp
var/cache/zoneminder/cache
usr/share/zoneminder/db
usr/share/zoneminder/fonts
etc/zm/
etc/zm/conf.d

View File

@ -5,6 +5,7 @@ usr/lib/zoneminder
usr/share/polkit-1
usr/share/zoneminder/db
usr/share/zoneminder/www
usr/share/zoneminder/fonts
# libzoneminder-perl files:
usr/share/man/man3

View File

@ -5,5 +5,6 @@ var/cache/zoneminder/images
var/cache/zoneminder/temp
var/cache/zoneminder/cache
usr/share/zoneminder/db
usr/share/zoneminder/fonts
etc/zm/
etc/zm/conf.d

View File

@ -5,6 +5,7 @@ usr/lib/zoneminder
usr/share/polkit-1
usr/share/zoneminder/db
usr/share/zoneminder/www
usr/share/zoneminder/fonts
# libzoneminder-perl files:
usr/share/man/man3

5
fonts/CMakeLists.txt Normal file
View File

@ -0,0 +1,5 @@
# Glob all database upgrade scripts
file(GLOB fontfileslist RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "*.zmfnt")
# Install the fonts
install(FILES ${fontfileslist} DESTINATION "${ZM_FONTDIR}")

BIN
fonts/default.zmfnt Normal file

Binary file not shown.

View File

@ -3780,6 +3780,14 @@ our @options = (
type => $types{boolean},
category => 'logging',
},
{
name => 'ZM_FONT_FILE_LOCATION',
default => '@ZM_FONTDIR@/default.zmfnt',
description => 'Font file location',
help => 'This font is used for timestamp labels.',
type => $types{string},
category => 'config',
},
);
our %options_hash = map { ( $_->{name}, $_ ) } @options;

View File

@ -32,6 +32,7 @@ require ZoneMinder::Base;
require ZoneMinder::Object;
require ZoneMinder::Storage;
require ZoneMinder::Frame;
require ZoneMinder::Monitor;
require Date::Manip;
require File::Find;
require File::Path;
@ -915,6 +916,15 @@ sub canEdit {
return 0;
} # end sub canEdit
sub Monitor {
my $self = shift;
$$self{Monitor} = shift if @_;
if ( !$$self{Monitor} ) {
$$self{Monitor} = new ZoneMinder::Monitor($$self{MonitorId});
}
return $$self{Monitor};
}
1;
__END__

View File

@ -92,11 +92,13 @@ sub Execute {
}
if ( $self->{HasDiskPercent} ) {
my $disk_percent = getDiskPercent($$self{Storage} ? $$self{Storage}->Path() : ());
$$self{Storage} = ZoneMinder::Storage->find_one() if ! $$self{Storage};
my $disk_percent = getDiskPercent($$self{Storage} ? $$self{Storage}->Path() : $Config{ZM_DIR_EVENTS});
$sql =~ s/zmDiskPercent/$disk_percent/g;
}
if ( $self->{HasDiskBlocks} ) {
my $disk_blocks = getDiskBlocks();
$$self{Storage} = ZoneMinder::Storage->find_one() if ! $$self{Storage};
my $disk_blocks = getDiskBlocks($$self{Storage} ? $$self{Storage}->Path() : $Config{ZM_DIR_EVENTS});
$sql =~ s/zmDiskBlocks/$disk_blocks/g;
}
if ( $self->{HasSystemLoad} ) {
@ -421,7 +423,7 @@ sub getDiskPercent {
}
sub getDiskBlocks {
my $command = 'df .';
my $command = 'df ' . ($_[0] ? $_[0] : '.');
my $df = qx( $command );
my $space = -1;
if ( $df =~ /\s(\d+)\s+\d+\s+\d+%/ms ) {

View File

@ -381,8 +381,10 @@ sub checkFilter {
} # end if AutoCopy
if ( $filter->{UpdateDiskSpace} ) {
if ( $$filter{LockRows} ) {
$ZoneMinder::Database::dbh->begin_work();
$Event->lock_and_load();
}
my $old_diskspace = $$Event{DiskSpace};
my $new_diskspace = $Event->DiskSpace(undef);
@ -394,7 +396,7 @@ sub checkFilter {
) {
$Event->save();
}
$ZoneMinder::Database::dbh->commit();
$ZoneMinder::Database::dbh->commit() if !$$filter{LockRows};
} # end if UpdateDiskSpace
} # end foreach event
ZoneMinder::Database::end_transaction($dbh, $in_transaction) if $$filter{LockRows};

View File

@ -91,6 +91,9 @@ while( 1 ) {
}
} # end if ZM_LOG_DATABASE_LIMIT
# Delete any sessions that are more ethan a week old. Limiting to 100 because mysql sucks
zmDbDo('DELETE FROM Sessions WHERE access < ? LIMIT 100', time - (60*60*24*7));
sleep($Config{ZM_STATS_UPDATE_INTERVAL});
} # end while (1)

View File

@ -4,7 +4,7 @@
configure_file(zm_config_data.h.in "${CMAKE_CURRENT_BINARY_DIR}/zm_config_data.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_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_frame.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_input.cpp zm_ffmpeg_camera.cpp zm_group.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_libvnc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.cpp zm_ffmpeg.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 zm_fifo.cpp zm_crypt.cpp)
set(ZM_BIN_SRC_FILES 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_frame.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_input.cpp zm_ffmpeg_camera.cpp zm_group.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_libvnc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.cpp zm_ffmpeg.cpp zm_font.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 zm_fifo.cpp zm_crypt.cpp)
# A fix for cmake recompiling the source files for every target.

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
//
// ZoneMinder Event Class Implementation, $Date$, $Revision$
// ZoneMinder Event Class Implementation
// Copyright (C) 2001-2008 Philip Coombes
//
// This program is free software; you can redistribute it and/or
@ -79,7 +79,6 @@ Event::Event(
start_time = now;
}
unsigned int state_id = 0;
zmDbRow dbrow;
if ( dbrow.fetch("SELECT Id FROM States WHERE IsActive=1") ) {
@ -356,7 +355,7 @@ bool Event::WriteFrameImage(
bool Event::WriteFrameVideo(
const Image *image,
const struct timeval timestamp,
VideoWriter* videow) {
VideoWriter* videow) const {
const Image* frameimg = image;
Image ts_image;

View File

@ -45,20 +45,19 @@ class Zone;
class Monitor;
class EventStream;
#define MAX_PRE_ALARM_FRAMES 16 // Maximum number of prealarm frames that can be stored
// Maximum number of prealarm frames that can be stored
#define MAX_PRE_ALARM_FRAMES 16
typedef uint64_t event_id_t;
typedef enum { NORMAL=0, BULK, ALARM } FrameType;
#include "zm_frame.h"
//
// Class describing events, i.e. captured periods of activity.
//
class Event {
friend class EventStream;
protected:
static int sd;
public:
typedef std::set<std::string> StringSet;
typedef std::map<std::string,StringSet> StringSetMap;
@ -109,7 +108,12 @@ class Event {
static bool OpenFrameSocket(int);
static bool ValidateFrameSocket(int);
Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent=false );
Event(
Monitor *p_monitor,
struct timeval p_start_time,
const std::string &p_cause,
const StringSetMap &p_noteSetMap,
bool p_videoEvent=false);
~Event();
uint64_t Id() const { return id; }
@ -121,16 +125,33 @@ class Event {
const struct timeval &EndTime() const { return end_time; }
bool SendFrameImage(const Image *image, bool alarm_frame=false);
bool WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame=false ) const;
bool WriteFrameVideo( const Image *image, const struct timeval timestamp, VideoWriter* videow );
bool WriteFrameImage(
Image *image,
struct timeval timestamp,
const char *event_file,
bool alarm_frame=false
) const;
bool WriteFrameVideo(
const Image *image,
const struct timeval timestamp,
VideoWriter* videow
) const;
void updateNotes(const StringSetMap &stringSetMap);
void AddFrames(int n_frames, Image **images, struct timeval **timestamps);
void AddFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_image=nullptr );
void AddFrame(
Image *image,
struct timeval timestamp,
int score=0,
Image *alarm_image=nullptr);
private:
void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps );
void AddFramesInternal(
int n_frames,
int start_frame,
Image **images,
struct timeval **timestamps);
void WriteDbFrames();
void UpdateFramesDelta(double offset);
bool SetPath(Storage *storage);
@ -138,7 +159,9 @@ class Event {
public:
static const char *getSubPath(struct tm *time) {
static char subpath[PATH_MAX] = "";
snprintf(subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec);
snprintf(subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d",
time->tm_year-100, time->tm_mon+1, time->tm_mday,
time->tm_hour, time->tm_min, time->tm_sec);
return subpath;
}
static const char *getSubPath(time_t *time) {
@ -149,7 +172,6 @@ class Event {
return video_file.c_str();
}
public:
static int PreAlarmCount() {
return pre_alarm_count;
}
@ -166,7 +188,12 @@ class Event {
}
pre_alarm_count = 0;
}
static void AddPreAlarmFrame(Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=nullptr) {
static void AddPreAlarmFrame(
Image *image,
struct timeval timestamp,
int score=0,
Image *alarm_frame=nullptr
) {
pre_alarm_data[pre_alarm_count].image = new Image(*image);
pre_alarm_data[pre_alarm_count].timestamp = timestamp;
pre_alarm_data[pre_alarm_count].score = score;

View File

@ -912,7 +912,7 @@ int FfmpegCamera::CaptureAndRecord(
delete queued_packet;
} // end while packets in the packetqueue
Debug(2, "Wrote %d queued packets", packet_count);
}
} // end if videostore is open or not
} // end if ! was recording
} else {
@ -976,6 +976,7 @@ int FfmpegCamera::CaptureAndRecord(
}
} // end if keyframe or have_video_keyframe
if ( monitor->DecodingEnabled() ) {
ret = zm_send_packet_receive_frame(mVideoCodecContext, mRawFrame, packet);
if ( ret < 0 ) {
if ( AVERROR(EAGAIN) != ret ) {
@ -1033,6 +1034,7 @@ int FfmpegCamera::CaptureAndRecord(
zm_av_packet_unref(&packet);
return -1;
}
} // end if don't need to do decode
frameComplete = 1;
frameCount++;

74
src/zm_font.cpp Normal file
View File

@ -0,0 +1,74 @@
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include "zm.h"
#include "zm_font.h"
#include "zm_utils.h"
int ZmFont::ReadFontFile(const std::string &loc) {
FILE *f = fopen(loc.c_str(), "rb");
if ( !f ) return -1; // FILE NOT FOUND
struct stat st;
stat(loc.c_str(), &st);
font = new ZMFONT;
size_t header_size = 8 + (sizeof(ZMFONT_BH) * NUM_FONT_SIZES);
// MAGIC + pad + BitmapHeaders
size_t readsize = fread(&font[0], 1, header_size, f);
if ( readsize < header_size ) {
delete font;
font = nullptr;
return -2; // EOF reached, invalid file
}
if ( memcmp(font->MAGIC, "ZMFNT", 5) != 0 ) // Check whether magic is correct
return -3;
for ( int i = 0; i < NUM_FONT_SIZES; i++ ) {
/* Character Width cannot be greater than 64 as a row is represented as a uint64_t,
height cannot be greater than 200(arbitary number which i have chosen, shouldn't need more than this) and
idx should not be more than filesize
*/
if ( (font->header[i].charWidth > 64 && font->header[i].charWidth == 0) ||
(font->header[i].charHeight > 200 && font->header[i].charHeight == 0) ||
(font->header[i].idx > st.st_size) ) {
delete font;
font = nullptr;
return -4;
}
} // end foreach font size
datasize = st.st_size - header_size;
font->data = new uint64_t[datasize/sizeof(uint64_t)];
readsize = fread(&font->data[0], 1, datasize, f);
if ( readsize < datasize ) { // Shouldn't happen
delete[] font->data;
font->data = nullptr;
delete font;
font = nullptr;
return -2;
}
fclose(f);
return 0;
}
ZmFont::~ZmFont() {
if ( font && font->data ) {
delete[] font->data;
font->data = nullptr;
}
if ( font ) {
delete font;
font = nullptr;
}
}
uint64_t *ZmFont::GetBitmapData() {
return &font->data[font->header[size].idx];
}

File diff suppressed because it is too large Load Diff

View File

@ -18,7 +18,6 @@
//
#include "zm.h"
#include "zm_font.h"
#include "zm_bigfont.h"
#include "zm_image.h"
#include "zm_utils.h"
#include "zm_rgb.h"
@ -78,6 +77,9 @@ static deinterlace_4field_fptr_t fptr_deinterlace_4field_gray8;
/* Pointer to image buffer memory copy function */
imgbufcpy_fptr_t fptr_imgbufcpy;
/* Font */
static ZmFont font;
void Image::update_function_pointers() {
/* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements */
if ( pixels % 16 || pixels % 12 ) {
@ -489,6 +491,12 @@ void Image::Initialise() {
g_u_table = g_u_table_global;
b_u_table = b_u_table_global;
int res = font.ReadFontFile(config.font_file_location);
if ( res == -1 ) {
Panic("Invalid font location: %s", config.font_file_location);
} else if ( res == -2 || res == -3 || res == -4 ) {
Panic("Invalid font file.");
}
initialised = true;
}
@ -1873,8 +1881,12 @@ const Coord Image::centreCoord( const char *text, int size=1 ) const {
line = text+index;
line_no++;
}
int x = (width - (max_line_len * ZM_CHAR_WIDTH * size) ) / 2;
int y = (height - (line_no * LINE_HEIGHT * size) ) / 2;
font.SetFontSize(size-1);
uint16_t char_width = font.GetCharWidth();
uint16_t char_height = font.GetCharHeight();
int x = (width - (max_line_len * char_width )) / 2;
int y = (height - (line_no * char_height) ) / 2;
return Coord(x, y);
}
@ -1920,8 +1932,16 @@ void Image::MaskPrivacy( const unsigned char *p_bitmask, const Rgb pixel_colour
}
/* RGB32 compatible: complete */
void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int size, const Rgb fg_colour, const Rgb bg_colour )
{
/* Bitmap decoding trick has been adopted from here:
https://lemire.me/blog/2018/02/21/iterating-over-set-bits-quickly/
*/
void Image::Annotate(
const char *p_text,
const Coord &coord,
const unsigned int size,
const Rgb fg_colour,
const Rgb bg_colour
) {
strncpy(text, p_text, sizeof(text)-1);
unsigned int index = 0;
@ -1935,7 +1955,6 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int
const uint8_t fg_b_col = BLUE_VAL_RGBA(fg_colour);
const uint8_t fg_bw_col = fg_colour & 0xff;
const Rgb fg_rgb_col = rgb_convert(fg_colour, subpixelorder);
const bool fg_trans = (fg_colour == RGB_TRANSPARENT);
const uint8_t bg_r_col = RED_VAL_RGBA(bg_colour);
const uint8_t bg_g_col = GREEN_VAL_RGBA(bg_colour);
@ -1944,21 +1963,23 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int
const Rgb bg_rgb_col = rgb_convert(bg_colour, subpixelorder);
const bool bg_trans = (bg_colour == RGB_TRANSPARENT);
int zm_text_bitmask = 0x80;
if ( size == 2 )
zm_text_bitmask = 0x8000;
font.SetFontSize(size-1);
const uint16_t char_width = font.GetCharWidth();
const uint16_t char_height = font.GetCharHeight();
const uint64_t *font_bitmap = font.GetBitmapData();
Debug(1, "Font size %d, char_width %d char_height %d", size, char_width, char_height);
while ( (index < text_len) && (line_len = strcspn(line, "\n")) ) {
unsigned int line_width = line_len * ZM_CHAR_WIDTH * size;
unsigned int line_width = line_len * char_width;
unsigned int lo_line_x = coord.X();
unsigned int lo_line_y = coord.Y() + (line_no * LINE_HEIGHT * size);
unsigned int lo_line_y = coord.Y() + (line_no * char_height);
unsigned int min_line_x = 0;
// FIXME What if line_width > width?
unsigned int max_line_x = width - line_width;
unsigned int min_line_y = 0;
unsigned int max_line_y = height - (LINE_HEIGHT * size);
unsigned int max_line_y = height - char_height;
if ( lo_line_x > max_line_x )
lo_line_x = max_line_x;
@ -1970,7 +1991,7 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int
lo_line_y = min_line_y;
unsigned int hi_line_x = lo_line_x + line_width;
unsigned int hi_line_y = lo_line_y + (LINE_HEIGHT * size);
unsigned int hi_line_y = lo_line_y + char_height;
// Clip anything that runs off the right of the screen
if ( hi_line_x > width )
@ -1980,99 +2001,78 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int
if ( colours == ZM_COLOUR_GRAY8 ) {
unsigned char *ptr = &buffer[(lo_line_y*width)+lo_line_x];
for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < (ZM_CHAR_HEIGHT * size); y++, r++, ptr += width ) {
for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < char_height; y++, r++, ptr += width ) {
unsigned char *temp_ptr = ptr;
for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) {
int f;
if ( size == 2 ) {
if ( (line[c] * ZM_CHAR_HEIGHT * size) + r > sizeof(bigfontdata) ) {
if ( line[c] > 0xFF ) {
Warning("Unsupported character %c in %s", line[c], line);
continue;
}
f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r];
} else {
if ( (line[c] * ZM_CHAR_HEIGHT) + r > sizeof(fontdata) ) {
Warning("Unsupported character %c in %s", line[c], line);
continue;
}
f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r];
}
for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr++ ) {
if ( f & (zm_text_bitmask >> i) ) {
if ( !fg_trans )
*temp_ptr = fg_bw_col;
} else if ( !bg_trans ) {
*temp_ptr = bg_bw_col;
}
uint64_t f = font_bitmap[(line[c] * char_height) + r];
if ( !bg_trans ) memset(temp_ptr, bg_bw_col, char_width);
while ( f != 0 ) {
uint64_t t = f & -f;
int idx = char_width - __builtin_ctzll(f);
*(temp_ptr + idx) = fg_bw_col;
f ^= t;
}
temp_ptr += char_width;
}
}
} else if ( colours == ZM_COLOUR_RGB24 ) {
unsigned int wc = width * colours;
unsigned char *ptr = &buffer[((lo_line_y*width)+lo_line_x)*colours];
for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < (ZM_CHAR_HEIGHT * size); y++, r++, ptr += wc ) {
for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < char_height; y++, r++, ptr += wc ) {
unsigned char *temp_ptr = ptr;
for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) {
int f;
if ( size == 2 ) {
if ( (line[c] * ZM_CHAR_HEIGHT * size) + r > sizeof(bigfontdata) ) {
if ( line[c] > 0xFF ) {
Warning("Unsupported character %c in %s", line[c], line);
continue;
}
f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r];
} else {
if ( (line[c] * ZM_CHAR_HEIGHT) + r > sizeof(fontdata) ) {
Warning("Unsupported character %c in %s", line[c], line);
continue;
}
f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r];
}
for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr += colours ) {
if ( f & (zm_text_bitmask >> i) ) {
if ( !fg_trans ) {
RED_PTR_RGBA(temp_ptr) = fg_r_col;
GREEN_PTR_RGBA(temp_ptr) = fg_g_col;
BLUE_PTR_RGBA(temp_ptr) = fg_b_col;
}
} else if ( !bg_trans ) {
RED_PTR_RGBA(temp_ptr) = bg_r_col;
GREEN_PTR_RGBA(temp_ptr) = bg_g_col;
BLUE_PTR_RGBA(temp_ptr) = bg_b_col;
uint64_t f = font_bitmap[(line[c] * char_height) + r];
if ( !bg_trans ) {
for ( int i = 0; i < char_width; i++ ) { // We need to set individual r,g,b components
unsigned char *colour_ptr = temp_ptr + (i*3);
RED_PTR_RGBA(colour_ptr) = bg_r_col;
GREEN_PTR_RGBA(colour_ptr) = bg_g_col;
BLUE_PTR_RGBA(colour_ptr) = bg_b_col;
}
}
while ( f != 0 ) {
uint64_t t = f & -f;
int idx = char_width - __builtin_ctzll(f);
unsigned char *colour_ptr = temp_ptr + (idx*3);
RED_PTR_RGBA(colour_ptr) = fg_r_col;
GREEN_PTR_RGBA(colour_ptr) = fg_g_col;
BLUE_PTR_RGBA(colour_ptr) = fg_b_col;
f ^= t;
}
temp_ptr += char_width * colours;
}
}
} else if ( colours == ZM_COLOUR_RGB32 ) {
unsigned int wc = width * colours;
uint8_t *ptr = &buffer[((lo_line_y*width)+lo_line_x) << 2];
for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < (ZM_CHAR_HEIGHT * size); y++, r++, ptr += wc ) {
for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < char_height; y++, r++, ptr += wc ) {
Rgb* temp_ptr = (Rgb*)ptr;
for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) {
int f;
if ( size == 2 ) {
if ( (line[c] * ZM_CHAR_HEIGHT * size) + r > sizeof(bigfontdata) ) {
if ( line[c] > 0xFF ) {
Warning("Unsupported character %c in %s", line[c], line);
continue;
}
f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r];
} else {
if ( (line[c] * ZM_CHAR_HEIGHT) + r > sizeof(fontdata) ) {
Warning("Unsupported character %c in %s", line[c], line);
continue;
}
f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r];
}
for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr++ ) {
if ( f & (zm_text_bitmask >> i) ) {
if ( !fg_trans ) {
*temp_ptr = fg_rgb_col;
}
} else if ( !bg_trans ) {
*temp_ptr = bg_rgb_col;
uint64_t f = font_bitmap[(line[c] * char_height) + r];
if ( !bg_trans ) {
for ( int i = 0; i < char_width; i++ )
*(temp_ptr + i) = bg_rgb_col;
}
while ( f != 0 ) {
uint64_t t = f & -f;
int idx = char_width - __builtin_ctzll(f);
*(temp_ptr + idx) = fg_rgb_col;
f ^= t;
}
temp_ptr += char_width;
}
}

View File

@ -71,8 +71,8 @@
// This is the official SQL (and ordering of the fields) to load a Monitor.
// It will be used whereever a Monitor dbrow is needed. WHERE conditions can be appended
std::string load_monitor_sql =
"SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Function`+0, `Enabled`, `LinkedMonitors`, "
"`AnalysisFPSLimit`, `AnalysisUpdateDelay`, `MaxFPS`, `AlarmMaxFPS`,"
"SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Function`+0, `Enabled`, `DecodingEnabled`, "
"`LinkedMonitors`, `AnalysisFPSLimit`, `AnalysisUpdateDelay`, `MaxFPS`, `AlarmMaxFPS`,"
"`Device`, `Channel`, `Format`, `V4LMultiBuffer`, `V4LCapturesPerFrame`, " // V4L Settings
"`Protocol`, `Method`, `Options`, `User`, `Pass`, `Host`, `Port`, `Path`, `Width`, `Height`, `Colours`, `Palette`, `Orientation`+0, `Deinterlacing`, "
"`DecoderHWAccelName`, `DecoderHWAccelDevice`, `RTSPDescribe`, "
@ -282,6 +282,7 @@ Monitor::Monitor(
const unsigned int p_storage_id,
int p_function,
bool p_enabled,
bool p_decoding_enabled,
const char *p_linked_monitors,
Camera *p_camera,
int p_orientation,
@ -326,6 +327,7 @@ Monitor::Monitor(
storage_id( p_storage_id ),
function( (Function)p_function ),
enabled( p_enabled ),
decoding_enabled(p_decoding_enabled),
width( (p_orientation==ROTATE_90||p_orientation==ROTATE_270)?p_camera->Height():p_camera->Width() ),
height( (p_orientation==ROTATE_90||p_orientation==ROTATE_270)?p_camera->Width():p_camera->Height() ),
orientation( (Orientation)p_orientation ),
@ -366,12 +368,15 @@ Monitor::Monitor(
purpose( p_purpose ),
last_motion_score(0),
camera( p_camera ),
event(0),
n_zones( p_n_zones ),
zones( p_zones ),
timestamps( nullptr ),
images( nullptr ),
privacy_bitmask( nullptr ),
event_delete_thread(nullptr)
event_delete_thread(nullptr),
n_linked_monitors(0),
linked_monitors(nullptr)
{
if ( analysis_fps > 0.0 ) {
uint64_t usec = round(1000000*pre_event_count/analysis_fps);
@ -458,6 +463,18 @@ Monitor::Monitor(
exit(-1);
}
// Do this here to save a few cycles with all the comparisons
decoding_enabled = !(
( function == RECORD or function == NODECT )
and
( savejpegs == 0 )
and
( videowriter == H264PASSTHROUGH )
and
!decoding_enabled
);
Debug(1, "Decoding enabled: %d", decoding_enabled);
memset(mem_ptr, 0, mem_size);
shared_data->size = sizeof(SharedData);
shared_data->active = enabled;
@ -532,7 +549,6 @@ Monitor::Monitor(
start_time = last_fps_time = time( 0 );
event = 0;
Debug(1, "Monitor %s has function %d,\n"
"label format = '%s', label X = %d, label Y = %d, label size = %d,\n"
@ -545,10 +561,6 @@ Monitor::Monitor(
//Set video recording flag for event start constructor and easy reference in code
videoRecording = ((GetOptVideoWriter() == H264PASSTHROUGH) && camera->SupportsNativeVideo());
n_linked_monitors = 0;
linked_monitors = nullptr;
} // Monitor::Monitor
bool Monitor::connect() {
@ -1432,7 +1444,8 @@ bool Monitor::Analyse() {
bool signal = shared_data->signal;
bool signal_change = (signal != last_signal);
Debug(3, "Motion detection is enabled signal(%d) signal_change(%d)", signal, signal_change);
Debug(3, "Motion detection is enabled signal(%d) signal_change(%d) trigger state(%d)",
signal, signal_change, trigger_data->trigger_state);
if ( trigger_data->trigger_state != TRIGGER_OFF ) {
unsigned int score = 0;
@ -1511,7 +1524,11 @@ bool Monitor::Analyse() {
for ( int i = 0; i < n_linked_monitors; i++ ) {
// TODO: Shouldn't we try to connect?
if ( linked_monitors[i]->isConnected() ) {
Debug(4, "Linked monitor %d %s is connected",
linked_monitors[i]->Id(), linked_monitors[i]->Name());
if ( linked_monitors[i]->hasAlarmed() ) {
Debug(4, "Linked monitor %d %s is alarmed",
linked_monitors[i]->Id(), linked_monitors[i]->Name());
if ( !event ) {
if ( first_link ) {
if ( cause.length() )
@ -1522,6 +1539,9 @@ bool Monitor::Analyse() {
}
noteSet.insert(linked_monitors[i]->Name());
score += 50;
} else {
Debug(4, "Linked monitor %d %s is not alarmed",
linked_monitors[i]->Id(), linked_monitors[i]->Name());
}
} else {
Debug(1, "Linked monitor %d %d is not connected. Connecting.", i, linked_monitors[i]->Id());
@ -1842,7 +1862,7 @@ bool Monitor::Analyse() {
image_count++;
return true;
} // end Monitor::Analyze
} // end Monitor::Analyse
void Monitor::Reload() {
Debug(1, "Reloading monitor %s", name);
@ -1855,7 +1875,7 @@ void Monitor::Reload() {
static char sql[ZM_SQL_MED_BUFSIZ];
// This seems to have fallen out of date.
snprintf(sql, sizeof(sql),
"SELECT `Function`+0, `Enabled`, `LinkedMonitors`, `EventPrefix`, `LabelFormat`, "
"SELECT `Function`+0, `Enabled`, `DecodingEnabled`, `LinkedMonitors`, `EventPrefix`, `LabelFormat`, "
"`LabelX`, `LabelY`, `LabelSize`, `WarmupCount`, `PreEventCount`, `PostEventCount`, "
"`AlarmFrameCount`, `SectionLength`, `MinSectionLength`, `FrameSkip`, "
"`MotionFrameSkip`, `AnalysisFPSLimit`, `AnalysisUpdateDelay`, `MaxFPS`, `AlarmMaxFPS`, "
@ -1871,6 +1891,7 @@ void Monitor::Reload() {
int index = 0;
function = (Function)atoi(dbrow[index++]);
enabled = atoi(dbrow[index++]);
decoding_enabled = atoi(dbrow[index++]);
const char *p_linked_monitors = dbrow[index++];
if ( dbrow[index] ) {
@ -2018,7 +2039,7 @@ void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) {
int n_monitors = mysql_num_rows(result);
if ( n_monitors == 1 ) {
MYSQL_ROW dbrow = mysql_fetch_row(result);
Debug(1, "Linking to monitor %d", link_ids[i]);
Debug(1, "Linking to monitor %d %s", atoi(dbrow[0]), dbrow[1]);
linked_monitors[count++] = new MonitorLink(link_ids[i], dbrow[1]);
} else {
Warning("Can't link to monitor %d, invalid id, function or not enabled", link_ids[i]);
@ -2129,6 +2150,7 @@ Monitor *Monitor::Load(MYSQL_ROW dbrow, bool load_zones, Purpose purpose) {
std::string type = dbrow[col] ? dbrow[col] : ""; col++;
Function function = (Function)atoi(dbrow[col]); col++;
int enabled = dbrow[col] ? atoi(dbrow[col]) : 0; col++;
int decoding_enabled = dbrow[col] ? atoi(dbrow[col]) : 0; col++;
const char *linked_monitors = dbrow[col];col++;
double analysis_fps = dbrow[col] ? strtod(dbrow[col], nullptr) : 0; col++;
@ -2405,6 +2427,7 @@ Monitor *Monitor::Load(MYSQL_ROW dbrow, bool load_zones, Purpose purpose) {
storage_id,
(int)function,
enabled,
decoding_enabled,
linked_monitors,
camera,
orientation,

View File

@ -253,6 +253,7 @@ protected:
CameraType type;
Function function; // What the monitor is doing
bool enabled; // Whether the monitor is enabled or asleep
bool decoding_enabled; // Whether the monitor will decode h264/h265 packets
unsigned int width; // Normally the same as the camera, but not if partly rotated
unsigned int height; // Normally the same as the camera, but not if partly rotated
bool v4l_multi_buffer;
@ -378,6 +379,7 @@ public:
unsigned int p_storage_id,
int p_function,
bool p_enabled,
bool p_decoding_enabled,
const char *p_linked_monitors,
Camera *p_camera,
int p_orientation,
@ -445,6 +447,9 @@ public:
return false;
return enabled;
}
inline bool DecodingEnabled() const {
return decoding_enabled;
}
inline const char *EventPrefix() const { return event_prefix; }
inline bool Ready() const {
if ( function <= MONITOR )

View File

@ -1 +1 @@
1.35.14
1.35.16

View File

@ -6,7 +6,7 @@ $data = array();
// INITIALIZE AND CHECK SANITY
//
if ( !canEdit('Events') ) $message = 'Insufficient permissions for user '.$user['Username'];
if ( !canView('Events') ) $message = 'Insufficient permissions for user '.$user['Username'];
if ( empty($_REQUEST['task']) ) {
$message = 'Must specify a task';
@ -74,10 +74,22 @@ if ( isset($_REQUEST['limit']) ) {
switch ( $task ) {
case 'archive' :
foreach ( $eids as $eid ) archiveRequest($task, $eid);
break;
case 'unarchive' :
# The idea is that anyone can archive, but only people with Event Edit permission can unarchive..
if ( !canEdit('Events') ) {
ajaxError('Insufficient permissions for user '.$user['Username']);
return;
}
foreach ( $eids as $eid ) archiveRequest($task, $eid);
break;
case 'delete' :
if ( !canEdit('Events') ) {
ajaxError('Insufficient permissions for user '.$user['Username']);
return;
}
foreach ( $eids as $eid ) $data[] = deleteRequest($eid);
break;
case 'query' :
@ -222,7 +234,7 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single', 'rate'=>'400'), '&amp;');
// Modify the row data as needed
$row['imgHtml'] = '<img id="thumbnail' .$event->Id(). '" src="' .$imgSrc. '" alt="' .validHtmlStr('Event ' .$event->Id()). '" style="width:' .validInt($event->ThumbnailWidth()). 'px;height:' .validInt($event->ThumbnailHeight()).'px;" stream_src="' .$streamSrc. '" still_src="' .$imgSrc. '"/>';
$row['imgHtml'] = '<img id="thumbnail' .$event->Id(). '" src="' .$imgSrc. '" alt="Event '.$event->Id().'" width="' .validInt($event->ThumbnailWidth()). '" height="' .validInt($event->ThumbnailHeight()).'" stream_src="' .$streamSrc. '" still_src="' .$imgSrc. '"/>';
$row['Name'] = validHtmlStr($row['Name']);
$row['Archived'] = $row['Archived'] ? translate('Yes') : translate('No');
$row['Emailed'] = $row['Emailed'] ? translate('Yes') : translate('No');

View File

@ -161,15 +161,30 @@ function queryRequest($eid, $search, $advsearch, $sort, $offset, $order, $limit)
$returned_rows = array();
foreach ( array_slice($filtered_rows, $offset, $limit) as $row ) {
if ( ZM_WEB_LIST_THUMBS ) {
# Build the path to the potential analysis image
$analImage = sprintf('%0'.ZM_EVENT_IMAGE_DIGITS.'d-analyse.jpg', $row['FrameId']);
$analPath = $Event->Path().'/'.$analImage;
$alarmFrame = $row['Type'] == 'Alarm';
$hasAnalImage = $alarmFrame && file_exists($analPath) && filesize($analPath);
# Our base img source component, which we will add on to
$base_img_src = '?view=image&amp;fid=' .$row['Id'];
# if an analysis images exists, use it as the thumbnail
if ( $hasAnalImage ) $base_img_src .= '&amp;show=analyse';
# Build the subcomponents needed for the image source
$ratio_factor = $Monitor->ViewHeight() / $Monitor->ViewWidth();
$thmb_width = ZM_WEB_LIST_THUMB_WIDTH ? 'width='.ZM_WEB_LIST_THUMB_WIDTH : '';
$thmb_height = 'height="'.( ZM_WEB_LIST_THUMB_HEIGHT ? ZM_WEB_LIST_THUMB_HEIGHT : ZM_WEB_LIST_THUMB_WIDTH*$ratio_factor ) .'"';
$thmb_fn = 'filename=' .$Event->MonitorId(). '_' .$row['EventId']. '_' .$row['FrameId']. '.jpg';
# Assemble the scaled and unscaled image source image source components
$img_src = join('&amp;', array_filter(array($base_img_src, $thmb_width, $thmb_height, $thmb_fn)));
$full_img_src = join('&amp;', array_filter(array($base_img_src, $thmb_fn)));
$frame_src = '?view=frame&amp;eid=' .$row['EventId']. '&amp;fid=' .$row['FrameId'];
# finally, we assemble the the entire thumbnail img src structure, whew
$row['Thumbnail'] = '<img src="' .$img_src. '" '.$thmb_width. ' ' .$thmb_height. 'img_src="' .$img_src. '" full_img_src="' .$full_img_src. '">';
}
$returned_rows[] = $row;

View File

@ -37,11 +37,39 @@ if ( !canEdit('Monitors') ) return;
</button>
</div>
<div class="modal-body">
<p>
<div class="form-group" id="FunctionFunction">
<label for="newFunction"><?php echo translate('Function') ?></label>
<?php echo htmlSelect('newFunction', ZM\getMonitorFunctionTypes(), null, array('id'=>'newFunction')); ?>
<label for="newEnabled"><?php echo translate('Enabled') ?></label>
<div id="function_help">
<?php
foreach ( ZM\getMonitorFunctionTypes() as $fn => $translated ) {
if ( isset($OLANG['FUNCTION_'.strtoupper($fn)]) ) {
echo '<div class="form-text" id="'.$fn.'Help">'.$OLANG['FUNCTION_'.strtoupper($fn)]['Help'].'</div>';
}
}
?>
</div>
</div>
<div class="form-group" id="FunctionAnalysisEnabled">
<label for="newEnabled"><?php echo translate('Analysis Enabled') ?></label>
<input type="checkbox" name="newEnabled" id="newEnabled" value="1"/>
</p>
<?php
if ( isset($OLANG['FUNCTION_ANALYSIS_ENABLED']) ) {
echo '<div class="form-text">'.$OLANG['FUNCTION_ANALYSIS_ENABLED']['Help'].'</div>';
}
?>
</div>
<div class="form-group" id="FunctionDecodingEnabled">
<label for="newDecodingEnabled"><?php echo translate('Decoding Enabled') ?></label>
<input type="checkbox" name="newDecodingEnabled" id="newDecodingEnabled" value="1"/>
<?php
if ( isset($OLANG['FUNCTION_DECODING_ENABLED']) ) {
echo '<div class="form-text">'.$OLANG['FUNCTION_DECODING_ENABLED']['Help'].'</div>';
}
?>
</div>
</div>
<div class="modal-footer">
<button type="button" class="funcSaveBtn btn btn-primary"><?php echo translate('Save') ?></button>

View File

@ -6,6 +6,7 @@ if ( empty($_REQUEST['fid']) ) ajaxError('Frame Id Not Provided');
$eid = $_REQUEST['eid'];
$fid = $_REQUEST['fid'];
$row = ( isset($_REQUEST['row']) ) ? $_REQUEST['row'] : '';
$raw = isset($_REQUEST['raw']);
$data = array();
// Not sure if this is required
@ -16,8 +17,28 @@ if ( ZM_OPT_USE_AUTH && (ZM_AUTH_RELAY == 'hashed') ) {
}
}
if ( $raw ) {
$sql = 'SELECT S.*,E.*,Z.Name AS ZoneName,Z.Units,Z.Area,M.Name AS MonitorName FROM Stats AS S LEFT JOIN Events AS E ON S.EventId = E.Id LEFT JOIN Zones AS Z ON S.ZoneId = Z.Id LEFT JOIN Monitors AS M ON E.MonitorId = M.Id WHERE S.EventId = ? AND S.FrameId = ? ORDER BY S.ZoneId';
$stat = dbFetchOne( $sql, NULL, array( $eid, $fid ) );
if ( $stat ) {
$stat['ZoneName'] = validHtmlStr($stat['ZoneName']);
$stat['PixelDiff'] = validHtmlStr($stat['PixelDiff']);
$stat['AlarmPixels'] = sprintf( "%d (%d%%)", $stat['AlarmPixels'], (100*$stat['AlarmPixels']/$stat['Area']) );
$stat['FilterPixels'] = sprintf( "%d (%d%%)", $stat['FilterPixels'], (100*$stat['FilterPixels']/$stat['Area']) );
$stat['BlobPixels'] = sprintf( "%d (%d%%)", $stat['BlobPixels'], (100*$stat['BlobPixels']/$stat['Area']) );
$stat['Blobs'] = validHtmlStr($stat['Blobs']);
if ( $stat['Blobs'] > 1 ) {
$stat['BlobSizes'] = sprintf( "%d-%d (%d%%-%d%%)", $stat['MinBlobSize'], $stat['MaxBlobSize'], (100*$stat['MinBlobSize']/$stat['Area']), (100*$stat['MaxBlobSize']/$stat['Area']) );
} else {
$stat['BlobSizes'] = sprintf( "%d (%d%%)", $stat['MinBlobSize'], 100*$stat['MinBlobSize']/$stat['Area'] );
}
$stat['AlarmLimits'] = validHtmlStr($stat['MinX'].",".$stat['MinY']."-".$stat['MaxX'].",".$stat['MaxY']);
}
$data['raw'] = $stat;
} else {
$data['html'] = getStatsTableHTML($eid, $fid, $row);
$data['id'] = '#contentStatsTable' .$row;
}
ajaxResponse($data);
return;

View File

@ -330,7 +330,7 @@ function collectData() {
foreach ( $postFuncs as $element=>$func )
$sqlData[$element] = eval('return( '.$func.'( $sqlData ) );');
$data[] = $sqlData;
if ( isset($limi) && ++$count >= $limit )
if ( isset($limit) && ++$count >= $limit )
break;
} # end foreach
} # end if have limit == 1
@ -364,6 +364,7 @@ switch ( $_REQUEST['layout'] ) {
$response = array( strtolower(validJsStr($_REQUEST['entity'])) => $data );
if ( isset($_REQUEST['loopback']) )
$response['loopback'] = validJsStr($_REQUEST['loopback']);
#ZM\Warning(print_r($response, true));
ajaxResponse($response);
break;
}

View File

@ -116,23 +116,23 @@ class GroupsController extends AppController {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
$this->Group->id = $id;
if ( $this->Group->save($this->request->data) ) {
return $this->flash(
__('The group has been saved.'),
array('action' => 'index')
);
$message = 'Saved';
} else {
$message = 'Error';
// if there is a validation message, use it
if ( !$this->group->validates() ) {
$message .= ': '.$this->Group->validationErrors;
}
} else {
$options = array('conditions' => array('Group.' . $this->Group->primaryKey => $id));
$this->request->data = $this->Group->find('first', $options);
}
$monitors = $this->Group->Monitor->find('list');
} # end if post/put
$group = $this->Group->findById($id);
$this->set(array(
'message' => $message,
'monitors'=> $monitors,
'_serialize' => array('message')
'group' => $group,
'_serialize' => array('group')
));
}

View File

@ -35,6 +35,7 @@ class Monitor extends ZM_Object {
'Type' => 'Ffmpeg',
'Function' => 'Mocord',
'Enabled' => array('type'=>'boolean','default'=>1),
'DecodingEnabled' => array('type'=>'boolean','default'=>1),
'LinkedMonitors' => array('type'=>'set', 'default'=>null),
'Triggers' => array('type'=>'set','default'=>''),
'ONVIF_URL' => '',

View File

@ -11,16 +11,8 @@ class ZM_Object {
$row = NULL;
if ( $IdOrRow ) {
global $object_cache;
if ( ! isset($object_cache[$class]) ) {
$object_cache[$class] = array();
}
$cache = &$object_cache[$class];
if ( is_integer($IdOrRow) or ctype_digit($IdOrRow) ) {
if ( isset($cache[$IdOrRow]) ) {
return $cache[$IdOrRow];
}
$table = $class::$table;
$row = dbFetchOne("SELECT * FROM `$table` WHERE `Id`=?", NULL, array($IdOrRow));
if ( !$row ) {
@ -34,6 +26,11 @@ class ZM_Object {
foreach ($row as $k => $v) {
$this->{$k} = $v;
}
global $object_cache;
if ( ! isset($object_cache[$class]) ) {
$object_cache[$class] = array();
}
$cache = &$object_cache[$class];
$cache[$row['Id']] = $this;
}
} # end if isset($IdOrRow)
@ -253,8 +250,8 @@ class ZM_Object {
} else if ( property_exists($this, $field) ) {
$type = (array_key_exists($field, $this->defaults) && is_array($this->defaults[$field])) ? $this->defaults[$field]['type'] : 'scalar';
if ( $type == 'set' ) {
$old_value = is_array($this->$field) ? $this->$field : explode(',', $this->$field);
$new_value = is_array($value) ? $value : explode(',', $value);
$old_value = is_array($this->$field) ? $this->$field : ($this->$field ? explode(',', $this->$field) : array());
$new_value = is_array($value) ? $value : ($value ? explode(',', $value) : array());
$diff = array_recursive_diff($old_value, $new_value);
if ( count($diff) ) {

View File

@ -55,6 +55,7 @@ if ( $action == 'save' ) {
'Controllable' => 0,
'TrackMotion' => 0,
'Enabled' => 0,
'DecodingEnabled' => 0,
'Exif' => 0,
'RTSPDescribe' => 0,
'V4LMultiBuffer' => '',
@ -82,7 +83,7 @@ if ( $action == 'save' ) {
}
}
$changes = $monitor->changes($_REQUEST['newMonitor'], $types);
$changes = $monitor->changes($_REQUEST['newMonitor']);
$restart = false;
if ( count($changes) ) {

View File

@ -105,7 +105,7 @@ function validateUser($username='', $password='') {
function userLogout() {
global $user;
ZM\Info('User "'.$user['Username'].'" logged out');
ZM\Info('User "'.($user?$user['Username']:'no one').'" logged out');
$user = null;// unset only clears the local variable
zm_session_clear();
}
@ -187,7 +187,26 @@ function getAuthUser($auth) {
} // end if $auth == $authHash
} // end foreach hour
} // end foreach user
if ( isset($_SESSION['username']) ) {
# In a multi-server case, we might be logged in as another user and so the auth hash didn't work
$sql = 'SELECT * FROM Users WHERE Enabled = 1 AND Username != ?';
foreach ( dbFetchAll($sql, NULL, $values) as $user ) {
$now = time();
for ( $i = 0; $i < ZM_AUTH_HASH_TTL; $i++, $now -= 3600 ) { // Try for last TTL hours
$time = localtime($now);
$authKey = ZM_AUTH_HASH_SECRET.$user['Username'].$user['Password'].$remoteAddr.$time[2].$time[3].$time[4].$time[5];
$authHash = md5($authKey);
if ( $auth == $authHash ) {
return $user;
} // end if $auth == $authHash
} // end foreach hour
} // end foreach user
} // end if
} // end if using auth hash
ZM\Error("Unable to authenticate user from auth hash '$auth'");
return null;
} // end getAuthUser($auth)

View File

@ -128,7 +128,7 @@ function dbEscape( $string ) {
return $dbConn->quote($string);
}
function dbQuery($sql, $params=NULL) {
function dbQuery($sql, $params=NULL, $debug = false) {
global $dbConn;
if ( dbLog($sql, true) )
return;
@ -145,7 +145,7 @@ function dbQuery($sql, $params=NULL) {
return NULL;
}
} else {
if ( defined('ZM_DB_DEBUG') ) {
if ( defined('ZM_DB_DEBUG') or $debug ) {
ZM\Debug("SQL: $sql values:" . ($params?implode(',',$params):''));
}
$result = $dbConn->query($sql);
@ -154,7 +154,7 @@ function dbQuery($sql, $params=NULL) {
return NULL;
}
}
if ( defined('ZM_DB_DEBUG') ) {
if ( defined('ZM_DB_DEBUG') or $debug ) {
ZM\Debug('SQL: '.$sql.' '.($params?implode(',',$params):'').' rows: '.$result->rowCount());
}
} catch(PDOException $e) {
@ -189,13 +189,13 @@ function dbFetchOne($sql, $col=false, $params=NULL) {
}
function dbFetchAll($sql, $col=false, $params=NULL) {
$dbRows = array();
$result = dbQuery($sql, $params);
if ( ! $result ) {
ZM\Error("SQL-ERR dbFetchAll no result, statement was '".$sql."'".($params ? 'params: '.join(',', $params) : ''));
return false;
return $dbRows;
}
$dbRows = array();
while ( $dbRow = $result->fetch(PDO::FETCH_ASSOC) )
$dbRows[] = $col ? $dbRow[$col] : $dbRow;
return $dbRows;

View File

@ -1200,8 +1200,8 @@ function sortHeader($field, $querySep='&amp;') {
'?view='.$view,
'page=1'.(isset($_REQUEST['filter'])?$_REQUEST['filter']['query']:''),
'sort_field='.$field,
'sort_asc='.($_REQUEST['sort_field'] == $field ? !$_REQUEST['sort_asc'] : 0),
'limit='.validInt($_REQUEST['limit']),
'sort_asc='.( ( isset($_REQUEST['sort_field']) and ( $_REQUEST['sort_field'] == $field ) ) ? !$_REQUEST['sort_asc'] : 0),
'limit='.(isset($_REQUEST['limit']) ? validInt($_REQUEST['limit']) : ''),
(isset($_REQUEST['eid']) ? 'eid='.$_REQUEST['eid'] : '' ),
));
}
@ -2156,7 +2156,8 @@ function human_filesize($size, $precision = 2) {
$size = $size / $step;
$i++;
}
return round($size, $precision).$units[$i];
# The idea is that we can right align this and have the digits columns line up nicely.
return sprintf('%.'.$precision.'f', round($size, $precision)).$units[$i];
}
function csrf_startup() {

View File

@ -1073,6 +1073,70 @@ $OLANG = array(
certainly not what you want! To unlink monitors you can ctrl-click.
'
),
'FUNCTION_NONE' => array(
'Help' => '
In None mode no processes are started. No capturing will occur.
'
),
'FUNCTION_MONITOR' => array(
'Help' => '
In Monitor mode the capture process (zmc) will connect to the camera and stream data.
It will be decoded if necessary and live viewing will be possible.
No motion detection will be performed. This monitor type cannot save video.
'
),
'FUNCTION_MODECT' => array(
'Help' => '
In Modect mode the capture process (zmc) will connect to the camera and stream data.
It will be decoded if necessary and live viewing will be possible.
In addition a process (zma) will analyse the video for motion.
When motion is detected, events will be created and video will be stored.
Motion data will be stored in the database for each event.
Events may also be triggered externally (zmtrigger) or by linked monitors.
'
),
'FUNCTION_RECORD' => array(
'Help' => '
In Record mode the capture process (zmc) will connect to the camera and stream data.
It will be decoded if necessary and live viewing will be possible.
In addition a process (zma) will run but will not perform motion detection.
Events will be created at fixed intervals and video will be stored.
'
),
'FUNCTION_MOCORD' => array(
'Help' => '
In Mocord mode the capture process (zmc) will connect to the camera and stream data.
It will be decoded if necessary and live viewing will be possible.
In addition a process (zma) will analyse the video for motion.
Events will be created at fixed intervals or at start and stop of motion.
Video will always be stored to disk and events will have the motion data stored in the database.
Events may also be triggered externally (zmtrigger) or by linked monitors.
'
),
'FUNCTION_NODECT' => array(
'Help' => '
In Nodect mode the capture process (zmc) will connect to the camera and stream data.
It will be decoded if necessary and live viewing will be possible.
In addition a process (zma) will run and will check any linked cameras for their alarm status.
When linked cameras or an external trigger (zmtrigger) are alarmed, events will be created
and video will be stored. No other motion detection will occur.
'
),
'FUNCTION_ANALYSIS_ENABLED' => array(
'Help' => '
When in Modect, Mocord, Nodect or RECORD mode the analysis process can be turned on/off.
This setting sets the default state when the process starts up.
It can then be turned on/off through external triggers zmtrigger zmu or the web ui.
When not enabled no motion detection or linked monitor checking will be performed and
no events will be created. The zma process will still be running waiting to be enabled.
'
),
'FUNCTION_DECODING_ENABLED' => array(
'Help' => '
When in Record or Nodect mode and using H264Passthrough with no jpegs being saved, we can
optionally choose to not decode the H264/H265 packets. This will drastically reduce cpu use
but will make live view unavailable for this monitor.'
),
// 'LANG_DEFAULT' => array(
// 'Prompt' => "This is a new prompt for this option",

View File

@ -728,23 +728,11 @@ li.search-choice {
}
.zoom {
padding: 50px;
transition: transform .2s; /* Animation */
margin: 0 auto;
}
.zoom:hover {
transform-origin: 70% 50%;
transform: scale(5); /* (arbitray zoom value - Note if the zoom is too large, it will go outside of the viewport) */
}
.zoom-right {
padding: 0px;
transition: transform .2s; /* Animation */
margin: 0 auto;
}
.zoom-right:hover {
.zoom-console {
transform-origin: 0% 50%;
transform: scale(5); /* (arbitray zoom value - Note if the zoom is too large, it will go outside of the viewport) */
}
@ -753,3 +741,6 @@ a.flip {
float: right;
margin-right: -20px;
}
#content table.major .colDiskSpace {
text-align: right;
}

View File

@ -99,3 +99,6 @@
.StatusFilter select {
min-width: 130px;
}
#FunctionFunction {
margin-bottom: 2rem;
}

View File

@ -42,3 +42,6 @@ input[name="newMonitor[Height]"] {
select.chosen {
width: 100%;
}
tr td:first-child {
min-width: 300px;
}

View File

@ -83,7 +83,7 @@ function exportEventDetail($event, $exportFrames, $exportImages) {
<tr><th scope="row"><?php echo translate('Monitor') ?></th><td><?php echo validHtmlStr($event->Monitor()->Name()) ?> (<?php echo $event->MonitorId() ?>)</td></tr>
<tr><th scope="row"><?php echo translate('Cause') ?></th><td><?php echo validHtmlStr($event->Cause()) ?></td></tr>
<tr><th scope="row"><?php echo translate('Notes') ?></th><td><?php echo validHtmlStr($event->Notes()) ?></td></tr>
<tr><th scope="row"><?php echo translate('Time') ?></th><td><?php echo strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->StartTime())) ?></td></tr>
<tr><th scope="row"><?php echo translate('Time') ?></th><td><?php echo strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->StartDateTime())) ?></td></tr>
<tr><th scope="row"><?php echo translate('Duration') ?></th><td><?php echo $event->Length() ?></td></tr>
<tr><th scope="row"><?php echo translate('Frames') ?></th><td><?php echo $event->Frames() ?></td></tr>
<tr><th scope="row"><?php echo translate('AttrAlarmFrames') ?></th><td><?php echo $event->AlarmFrames() ?></td></tr>
@ -999,5 +999,5 @@ function exportEvents(
unlink($monitorPath.'/'.$html_eventMaster);
}
return '?view=archive%26type='.$exportFormat.'%26connkey='.$connkey;
return '?view=archive&type='.$exportFormat.'&connkey='.$connkey;
} // end function exportEvents

View File

@ -497,16 +497,16 @@ function getBandwidthHTML($bandwidth_options, $user) {
}
$result = '<li id="getBandwidthHTML" class="nav-item dropdown mx-2">'.PHP_EOL;
$result .= '<a class="dropdown-toggle mr-2" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="material-icons md-18 mr-1">network_check</i>'.translate($bandwidth_options[$_COOKIE['zmBandwidth']]).'</a>'.PHP_EOL;
$result .= '<a class="dropdown-toggle mr-2" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="dropdown_bandwidth"><i class="material-icons md-18 mr-1">network_check</i>'.translate($bandwidth_options[$_COOKIE['zmBandwidth']]).'</a>'.PHP_EOL;
$result .= '<div class="dropdown-menu" id="dropdown_bandwidth" aria-labelledby="dropdown_bandwidth">'.PHP_EOL;
$result .= '<div class="dropdown-menu" aria-labelledby="dropdown_bandwidth">'.PHP_EOL;
if ( count($bandwidth_options) > 1 ) {
if ( isset($bandwidth_options['high']) )
$result .= '<a data-pdsa-dropdown-val="high" class="dropdown-item" href="#">' .translate('High'). '</a>'.PHP_EOL;
$result .= '<a data-pdsa-dropdown-val="high" class="dropdown-item bwselect" href="#">' .translate('High'). '</a>'.PHP_EOL;
if ( isset($bandwidth_options['medium']) )
$result .= '<a data-pdsa-dropdown-val="medium" class="dropdown-item" href="#">' .translate('Medium'). '</a>'.PHP_EOL;
$result .= '<a data-pdsa-dropdown-val="medium" class="dropdown-item bwselect" href="#">' .translate('Medium'). '</a>'.PHP_EOL;
# low is theoretically always available
$result .= '<a data-pdsa-dropdown-val="low" class="dropdown-item" href="#">' .translate('Low'). '</a>'.PHP_EOL;
$result .= '<a data-pdsa-dropdown-val="low" class="dropdown-item bwselect" href="#">' .translate('Low'). '</a>'.PHP_EOL;
}
$result .= '</div>'.PHP_EOL;
@ -871,8 +871,8 @@ function xhtmlFooter() {
<script src="tools/mootools/mootools-more.js"></script>
<script src="js/mootools.ext.js"></script>
<?php } ?>
<script src="skins/<?php echo $skin; ?>/js/jquery.js"></script>
<script src="skins/<?php echo $skin; ?>/js/jquery-ui-1.12.1/jquery-ui.js"></script>
<script src="skins/<?php echo $skin; ?>/js/jquery.min.js"></script>
<script src="skins/<?php echo $skin; ?>/js/jquery-ui-1.12.1/jquery-ui.min.js"></script>
<script src="skins/<?php echo $skin; ?>/js/bootstrap.min.js"></script>
<?php echo output_script_if_exists(array(
'js/bootstrap-table.min.js',

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
jquery-2.2.4.js

View File

@ -1 +0,0 @@
https://code.jquery.com/jquery-2.2.4.js

1
web/skins/classic/js/jquery.min.js vendored Symbolic link
View File

@ -0,0 +1 @@
jquery-3.5.1.min.js

View File

@ -0,0 +1 @@
https://code.jquery.com/jquery-3.5.1.min.js

View File

@ -241,7 +241,7 @@ if ( currentView != 'none' && currentView != 'login' ) {
$j(document).ready(function() {
// Load the Logout and State modals into the dom
$j('#logoutButton').click(clickLogout);
if ( canEditSystem ) $j('#stateModalBtn').click(getStateModal);
if ( canEdit.System ) $j('#stateModalBtn').click(getStateModal);
// Trigger autorefresh of the widget bar stats on the navbar
if ( $j('.navbar').length ) {
@ -639,7 +639,7 @@ function delCookie(name) {
}
function bwClickFunction() {
$j("#dropdown_bandwidth a").click(function() {
$j('.bwselect').click(function() {
var bwval = $j(this).data('pdsa-dropdown-val');
setCookie("zmBandwidth", bwval, 3600);
getNavBar();
@ -830,10 +830,11 @@ function startDownload( exportFile ) {
}
function exportResponse(data, responseText) {
console.log(data);
console.log('exportResponse data: ' + JSON.stringify(data));
var generated = (data.result=='Ok') ? 1 : 0;
var exportFile = '?view=archive&type='+data.exportFormat+'&connkey='+data.connkey;
//var exportFile = '?view=archive&type='+data.exportFormat+'&connkey='+data.connkey;
var exportFile = data.exportFile;
$j('#exportProgress').removeClass( 'text-warning' );
if ( generated ) {
@ -888,3 +889,33 @@ function manageShutdownBtns(element) {
})
.fail(logAjaxFail);
}
function thumbnail_onmouseover(event) {
timeout = setTimeout(function() {
var img = event.target;
var imgClass = ( currentView == 'console' ) ? 'zoom-console' : 'zoom';
var imgAttr = ( currentView == 'frames' ) ? 'full_img_src' : 'stream_src';
img.src = '';
img.src = img.getAttribute(imgAttr);
img.addClass(imgClass);
}, 350);
}
function thumbnail_onmouseout(event) {
clearTimeout(timeout);
var img = event.target;
var imgClass = ( currentView == 'console' ) ? 'zoom-console' : 'zoom';
var imgAttr = ( currentView == 'frames' ) ? 'img_src' : 'still_src';
img.src = '';
img.src = img.getAttribute(imgAttr);
img.removeClass(imgClass);
}
function initThumbAnimation() {
if ( ANIMATE_THUMBS ) {
$j('.colThumbnail img').each(function() {
this.addEventListener('mouseover', thumbnail_onmouseover, false);
this.addEventListener('mouseout', thumbnail_onmouseout, false);
});
}
}

View File

@ -40,14 +40,19 @@ var thisUrl = '<?php echo ZM_BASE_URL.preg_replace('/\.php.*$/i', '.php', $_SERV
var skinPath = '<?php echo ZM_SKIN_PATH ?>';
var serverId = '<?php echo defined('ZM_SERVER_ID') ? ZM_SERVER_ID : '' ?>';
var canEditSystem = <?php echo canEdit('System')?'true':'false' ?>;
var canViewSystem = <?php echo canView('System')?'true':'false' ?>;
var canEditEvents = <?php echo canEdit('Events')?'true':'false' ?>;
var canViewEvents = <?php echo canView('Events')?'true':'false' ?>;
var canEditMonitors = <?php echo canEdit('Monitors')?'true':'false' ?>;
var canViewMonitors = <?php echo canView('Monitors')?'true':'false' ?>;
var canView = {};
var canEdit = {};
<?php
$perms = array("Stream", "Events", "Control", "Monitors", "Groups", "System", "Devices");
foreach ( $perms as $perm ) {
?>
canView["<?php echo $perm ?>"] = <?php echo canView($perm)?'true':'false' ?>;
canEdit["<?php echo $perm ?>"] = <?php echo canEdit($perm)?'true':'false' ?>;
<?php
}
?>
var canEditGroups = <?php echo canEdit('Groups')?'true':'false' ?>;
var ANIMATE_THUMBS = <?php echo ZM_WEB_ANIMATE_THUMBS?'true':'false' ?>;
var refreshParent = <?php
if ( ! empty($refreshParent) ) {

View File

@ -82,10 +82,10 @@ if ( $groupSql )
foreach ( array('ServerId','StorageId','Status','Function') as $filter ) {
if ( isset($_SESSION[$filter]) ) {
if ( is_array($_SESSION[$filter]) ) {
$conditions[] = $filter . ' IN ('.implode(',', array_map(function(){return '?';}, $_SESSION[$filter])). ')';
$conditions[] = '`'.$filter . '` IN ('.implode(',', array_map(function(){return '?';}, $_SESSION[$filter])). ')';
$values = array_merge($values, $_SESSION[$filter]);
} else {
$conditions[] = $filter . '=?';
$conditions[] = '`'.$filter . '`=?';
$values[] = $_SESSION[$filter];
}
}

View File

@ -296,7 +296,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
$imgHTML='';
if ( ZM_WEB_LIST_THUMBS && ($monitor['Status'] == 'Connected') && $running ) {
$options = array();
$thmbClass = ZM_WEB_ANIMATE_THUMBS ? 'colThumbnail zoom-right' : 'colThumbnail';
$ratio_factor = $Monitor->ViewHeight() / $Monitor->ViewWidth();
$options['width'] = ZM_WEB_LIST_THUMB_WIDTH;
$options['height'] = ZM_WEB_LIST_THUMB_HEIGHT ? ZM_WEB_LIST_THUMB_HEIGHT : ZM_WEB_LIST_THUMB_WIDTH*$ratio_factor;
@ -309,7 +309,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
$thmbWidth = ( $options['width'] ) ? 'width:'.$options['width'].'px;' : '';
$thmbHeight = ( $options['height'] ) ? 'height:'.$options['height'].'px;' : '';
$imgHTML = '<div class="'.$thmbClass.'"><a';
$imgHTML = '<div class="colThumbnail"><a';
$imgHTML .= $stream_available ? ' href="?view=watch&amp;mid='.$monitor['Id'].'">' : '>';
$imgHTML .= '<img id="thumbnail' .$Monitor->Id(). '" src="' .$stillSrc. '" style="'
.$thmbWidth.$thmbHeight. '" stream_src="' .$streamSrc. '" still_src="' .$stillSrc. '"'.

View File

@ -42,7 +42,7 @@ xhtmlHeaders(__FILE__, translate('ControlCaps'));
<button id="deleteBtn" class="btn btn-danger" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Delete') ?>" disabled><i class="fa fa-trash"></i></button>
</div>
<div id="content">
<div id="content" class="table-responsive-sm">
<table
id="controlTable"
data-locale="<?php echo i18n() ?>"
@ -54,7 +54,6 @@ xhtmlHeaders(__FILE__, translate('ControlCaps'));
data-remember-order="true"
data-click-to-select="true"
data-maintain-meta-data="true"
data-mobile-responsive="true"
data-buttons-class="btn btn-normal"
data-toolbar="#toolbar"
data-show-columns="true"

View File

@ -46,7 +46,7 @@ xhtmlHeaders(__FILE__, translate('Devices') );
<button id="deleteBtn" class="btn btn-danger" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Delete') ?>" disabled><i class="fa fa-trash"></i></button>
</div>
<div id="content" class="row justify-content-center">
<div id="content" class="row justify-content-center table-responsive-sm">
<table
id="devicesTable"
data-locale="<?php echo i18n() ?>"
@ -59,7 +59,6 @@ xhtmlHeaders(__FILE__, translate('Devices') );
data-remember-order="true"
data-click-to-select="true"
data-maintain-meta-data="true"
data-mobile-responsive="true"
data-buttons-class="btn btn-normal"
data-toolbar="#toolbar"
data-show-columns="true"

View File

@ -131,8 +131,6 @@ if ( !$Event->Id() ) {
} else {
if ( !file_exists($Event->Path()) )
echo '<div class="error">Event was not found at '.$Event->Path().'. It is unlikely that playback will be possible.</div>';
$storage = validHtmlStr($Event->Storage()->Name()).( $Event->SecondaryStorageId() ? ', '.validHtmlStr($Event->SecondaryStorage()->Name()) : '' );
?>
<!-- BEGIN HEADER -->
@ -164,64 +162,7 @@ if ( !$Event->Id() ) {
<div class="">
<!-- VIDEO STATISTICS TABLE -->
<table id="eventStatsTable" class="table-sm table-borderless">
<tbody>
<tr>
<th class="text-right"><?php echo translate('EventId') ?></th>
<td id="dataEventId"><?php echo $Event->Id() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('EventName') ?></th>
<td id="dataEventName"><?php echo $Event->Name() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrMonitorId') ?></th>
<td id="dataMonitorId"><?php echo $Monitor->Id() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrMonitorName') ?></th>
<td id="dataMonitorName"><?php echo validHtmlStr($Monitor->Name()) ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('Cause') ?></th>
<td id="dataCause"><?php echo validHtmlStr($Event->Cause()) ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrStartTime') ?></th>
<td id="dataStartTime"><?php echo strftime(STRF_FMT_DATETIME_SHORT, strtotime($Event->StartDateTime())) ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('Duration') ?></th>
<td id="dataDuration"><?php echo $Event->Length().'s' ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrFrames') ?></th>
<td id="dataFrames"><?php echo $Event->Frames() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrAlarmFrames') ?></th>
<td id="dataAlarmFrames"><?php echo $Event->AlarmFrames() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrTotalScore') ?></th>
<td id="dataTotalScore"><?php echo $Event->TotScore() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrAvgScore') ?></th>
<td id="dataAvgScore"><?php echo $Event->AvgScore() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrMaxScore') ?></th>
<td id="dataMaxScore"><?php echo $Event->MaxScore() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('DiskSpace') ?></th>
<td id="dataDiskSpace"><?php echo human_filesize($Event->DiskSpace(null)) ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('Storage') ?></th>
<td id="dataStorage"><?php echo $storage?></td>
</tr>
</tbody>
<!-- EVENT STATISTICS POPULATED BY JAVASCRIPT -->
</table>
</div>
<div class="">

View File

@ -87,7 +87,6 @@ getBodyTopHTML();
data-show-fullscreen="true"
data-click-to-select="true"
data-maintain-meta-data="true"
data-mobile-responsive="true"
data-buttons-class="btn btn-normal"
data-show-jump-to="true"
data-show-refresh="true"

View File

@ -143,19 +143,19 @@ $event_count = 0;
while ( $event_row = dbFetchNext($results) ) {
$event = new ZM\Event($event_row);
$scale = max(reScale(SCALE_BASE, $event->Monitor()->DefaultScale(), ZM_WEB_DEFAULT_SCALE), SCALE_BASE);
$event_link = '?view=event&amp;eid='.$event->Id().$filterQuery.$sortQuery.'&amp;page=1';
?>
<tr<?php echo $event->Archived() ? ' class="archived"' : '' ?>>
<td class="colId">
<input type="hidden" name="eids[]" value="<?php echo $event->Id()?>"/>
<a href="?view=event&amp;eid=<?php echo $event->Id().$filterQuery.$sortQuery ?>&amp;page=1"><?php echo $event->Id().($event->Archived()?'*':'') ?></a>
<a href="<?php echo $event_link ?>"><?php echo $event->Id().($event->Archived()?'*':'') ?></a>
</td>
<td class="colName"><a href="?view=event&amp;eid=<?php echo $event->Id().$filterQuery.$sortQuery ?>&amp;page=1"><?php echo validHtmlStr($event->Name()).($event->Archived()?'*':'') ?></a></td>
<td class="colName"><a href="<?php echo $event_link ?>"><?php echo validHtmlStr($event->Name()).($event->Archived()?'*':'') ?></a></td>
<td class="colMonitorName"><?php echo makeLink('?view=monitor&amp;mid='.$event->MonitorId(), $event->MonitorName(), canEdit('Monitors')) ?></td>
<td class="colCause"><?php echo makeLink( '#', validHtmlStr($event->Cause()), canEdit( 'Events' ), 'title="' .htmlspecialchars($event->Notes()). '" class="eDetailLink" data-eid=' .$event->Id(). '"') ?></td>
<td class="colCause"><?php echo makeLink($event_link, validHtmlStr($event->Cause()), canView('Events'), 'title="' .htmlspecialchars($event->Notes()). '" class="eDetailLink" data-eid="'.$event->Id().'"') ?></td>
<td class="colTime"><?php echo strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->StartDateTime())) .
( $event->EndDateTime() ? ' until ' . strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->EndDateTime()) ) : '' ) ?>
</td>
<td class="colDuration"><?php echo gmdate("H:i:s", $event->Length() ) ?></td>
( $event->EndDateTime() ? ' until ' . strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->EndDateTime())) : '' ) ?></td>
<td class="colDuration"><?php echo gmdate('H:i:s', $event->Length()) ?></td>
<td class="colFrames"><?php echo makeLink('?view=frames&amp;eid='.$event->Id(), $event->Frames()) ?></td>
<td class="colAlarmFrames"><?php echo makeLink('?view=frames&amp;eid='.$event->Id(), $event->AlarmFrames()) ?></td>
<td class="colTotScore"><?php echo $event->TotScore() ?></td>
@ -168,9 +168,7 @@ while ( $event_row = dbFetchNext($results) ) {
echo '<td class="colDiskSpace">'.human_filesize($event->DiskSpace()).'</td>';
}
unset($event);
echo '
</tr>
';
echo PHP_EOL.'</tr>'.PHP_EOL;
} # end foreach event
?>
</tbody>
@ -179,9 +177,7 @@ while ( $event_row = dbFetchNext($results) ) {
<td colspan="11"><?php echo $event_count ?> events</td>
<?php
if ( ZM_WEB_EVENT_DISK_SPACE ) {
?>
<td class="colDiskSpace"><?php echo human_filesize($disk_space_total);?></td>
<?php
echo '<td class="colDiskSpace">'.human_filesize($disk_space_total).'</td>'.PHP_EOL;
}
?>
</tr>

View File

@ -49,7 +49,7 @@ $prevFid = $fid-1;
$nextFid = $fid+1;
$lastFid = $maxFid;
$alarmFrame = $Frame->Type() == 'Alarm';
$alarmFrame = ( $Frame->Type() == 'Alarm' ) ? 1 : 0;
if ( isset($_REQUEST['scale']) ) {
$scale = validNum($_REQUEST['scale']);
@ -103,7 +103,14 @@ xhtmlHeaders(__FILE__, translate('Frame').' - '.$Event->Id().' - '.$Frame->Frame
</form>
</div>
<div id="content">
<div id="content" class="d-flex flex-row justify-content-center">
<table id="frameStatsTable" class="table-sm table-borderless pr-3">
<!-- FRAME STATISTICS POPULATED BY AJAX -->
</table>
<div>
<p id="image">
<?php if ( $imageData['hasAnalImage'] ) {
echo sprintf('<a href="?view=frame&amp;eid=%d&amp;fid=%d&scale=%d&amp;show=%s">', $Event->Id(), $Frame->FrameId(), $scale, ( $show=='anal'?'capt':'anal' ) );
@ -152,6 +159,7 @@ if ( file_exists($rImagePath) ) {
class="<?php echo $imageData['imageClass'] ?>"
/>
</p>
</div>
<?php } ?>
</div>
</div>

View File

@ -41,7 +41,7 @@ xhtmlHeaders(__FILE__, translate('Frames').' - '.$Event->Id());
</div>
<!-- Table styling handled by bootstrap-tables -->
<div class="row justify-content-center">
<div class="row justify-content-center table-responsive-sm">
<table
id="framesTable"
data-locale="<?php echo i18n() ?>"
@ -60,7 +60,6 @@ xhtmlHeaders(__FILE__, translate('Frames').' - '.$Event->Id());
data-toolbar="#toolbar"
data-show-fullscreen="true"
data-maintain-meta-data="true"
data-mobile-responsive="true"
data-buttons-class="btn btn-normal"
data-detail-view="true"
data-detail-formatter="detailFormatter"

View File

@ -1,24 +1,3 @@
function thumbnail_onmouseover(event) {
var img = event.target;
img.src = '';
img.src = img.getAttribute('stream_src');
}
function thumbnail_onmouseout(event) {
var img = event.target;
img.src = '';
img.src = img.getAttribute('still_src');
}
function initThumbAnimation() {
if ( WEB_ANIMATE_THUMBS ) {
$j('.colThumbnail img').each(function() {
this.addEventListener('mouseover', thumbnail_onmouseover, false);
this.addEventListener('mouseout', thumbnail_onmouseout, false);
});
}
}
function setButtonStates( element ) {
var form = element.form;
var checked = 0;
@ -133,7 +112,7 @@ function reloadWindow() {
function manageFunctionModal(evt) {
evt.preventDefault();
if ( !canEditEvents ) {
if ( !canEdit.Events ) {
enoperm();
return;
}
@ -172,8 +151,25 @@ function manageFunctionModal(evt) {
console.error("Unable to find form with id function_form");
return;
}
function_form.elements['newFunction'].onchange=function() {
$j('#function_help div').hide();
$j('#'+this.value+'Help').show();
if ( this.value == 'Monitor' || this.value == 'None' ) {
$j('#FunctionAnalysisEnabled').hide();
} else {
$j('#FunctionAnalysisEnabled').show();
}
if ( this.value == 'Record' || this.value == 'Nodect' ) {
$j('#FunctionDecodingEnabled').show();
} else {
$j('#FunctionDecodingEnabled').hide();
}
};
function_form.elements['newFunction'].value = monitor.Function;
function_form.elements['newFunction'].onchange();
function_form.elements['newEnabled'].checked = monitor.Enabled == '1';
function_form.elements['newDecodingEnabled'].checked = monitor.DecodingEnabled == '1';
function_form.elements['mid'].value = mid;
document.getElementById('function_monitor_name').innerHTML = monitor.Name;

View File

@ -1,5 +1,4 @@
var consoleRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_MAIN ?>;
var WEB_ANIMATE_THUMBS = <?php echo ZM_WEB_ANIMATE_THUMBS?'true':'false' ?>;
<?php
if ( canEdit('System') && ZM_DYN_SHOW_DONATE_REMINDER ) {
@ -25,7 +24,8 @@ var monitors = new Array();
'Url': '<?php echo $monitor->UrlToIndex( ZM_MIN_STREAMING_PORT ? ($monitor->Id() + ZM_MIN_STREAMING_PORT) : '') ?>',
'Type': '<?php echo $monitor->Type() ?>',
'Function': '<?php echo $monitor->Function() ?>',
'Enabled': '<?php echo $monitor->Enabled() ?>'
'Enabled': '<?php echo $monitor->Enabled() ?>',
'DecodingEnabled': '<?php echo $monitor->DecodingEnabled() ?>'
};
<?php
}

View File

@ -43,7 +43,7 @@ function getDelConfirmModal(key) {
// Manage the DELETE CONFIRMATION modal button
function manageDelConfirmModalBtns() {
document.getElementById("delConfirmBtn").addEventListener("click", function onDelConfirmClick(evt) {
if ( ! canEditControl ) {
if ( ! canEdit.Control ) {
enoperm();
return;
}
@ -67,9 +67,9 @@ function initPage() {
function() {
selections = table.bootstrapTable('getSelections');
addNewBtn.prop('disabled', (selections.length || !canEditControl));
editBtn.prop('disabled', !((selections.length == 1) && canEditControl));
deleteBtn.prop('disabled', !(selections.length && canEditControl));
addNewBtn.prop('disabled', (selections.length || !canEdit.Control));
editBtn.prop('disabled', !((selections.length == 1) && canEdit.Control));
deleteBtn.prop('disabled', !(selections.length && canEdit.Control));
});
// Init the bootstrap-table
@ -92,7 +92,7 @@ function initPage() {
// Manage the DELETE button
document.getElementById("deleteBtn").addEventListener("click", function onDeleteClick(evt) {
if ( ! canEditControl ) {
if ( ! canEdit.Control ) {
enoperm();
return;
}

View File

@ -1 +0,0 @@
var canEditControl = <?php echo canEdit('Control')?'true':'false' ?>;

View File

@ -42,7 +42,7 @@ function getDelConfirmModal(key) {
// Manage the DELETE CONFIRMATION modal button
function manageDelConfirmModalBtns() {
document.getElementById("delConfirmBtn").addEventListener("click", function onDelConfirmClick(evt) {
if ( ! canEditDevice ) {
if ( ! canEdit.Device ) {
enoperm();
return;
}
@ -91,9 +91,9 @@ function initPage() {
// Init the bootstrap-table
table.bootstrapTable({icons: icons});
if ( canEditDevice ) enableDeviceModal();
if ( canEdit.Device ) enableDeviceModal();
newDeviceBtn.prop('disabled', !canEditDevice);
newDeviceBtn.prop('disabled', !canEdit.Device);
// Manage the BACK button
document.getElementById("backBtn").addEventListener("click", function onBackClick(evt) {
@ -112,7 +112,7 @@ function initPage() {
// Manage the DELETE button
document.getElementById("deleteBtn").addEventListener("click", function onDeleteClick(evt) {
if ( ! canEditDevice ) {
if ( ! canEdit.Device ) {
enoperm();
return;
}
@ -130,7 +130,7 @@ function initPage() {
function() {
selections = table.bootstrapTable('getSelections');
deleteBtn.prop('disabled', !(selections.length && canEditDevice));
deleteBtn.prop('disabled', !(selections.length && canEdit.Device));
});
// Process mouse clicks on the table cells

View File

@ -1 +0,0 @@
var canEditDevice = <?php echo canEdit('Devices') ? 'true' : 'false' ?>;

View File

@ -598,8 +598,8 @@ function getEventResponse(respObj, respText) {
$j('dataStorage').text( eventData.Storage );
// Refresh the status of the archive buttons
archiveBtn.prop('disabled', !(!eventData.Archived && canEditEvents));
unarchiveBtn.prop('disabled', !(eventData.Archived && canEditEvents));
archiveBtn.prop('disabled', !(!eventData.Archived && canEdit.Events));
unarchiveBtn.prop('disabled', !(eventData.Archived && canEdit.Events));
history.replaceState(null, null, '?view=event&eid=' + eventData.Id + filterQuery + sortQuery); //if popup removed, check if this allows forward
// Technically, events can be different sizes, so may need to update the size of the image, but it might be better to have it stay scaled...
@ -1032,20 +1032,10 @@ function handleClick( event ) {
}
}
// Load the Delete Confirmation Modal HTML via Ajax call
function getDelConfirmModal() {
$j.getJSON(thisUrl + '?request=modal&modal=delconfirm')
.done(function(data) {
insertModalHtml('deleteConfirm', data.html);
manageDelConfirmModalBtns();
})
.fail(logAjaxFail);
}
// Manage the DELETE CONFIRMATION modal button
function manageDelConfirmModalBtns() {
document.getElementById("delConfirmBtn").addEventListener("click", function onDelConfirmClick(evt) {
if ( ! canEditEvents ) {
if ( ! canEdit.Events ) {
enoperm();
return;
}
@ -1065,22 +1055,34 @@ function manageDelConfirmModalBtns() {
}
function getEvtStatsCookie() {
var cookie = 'zmEventStats'
var cookie = 'zmEventStats';
var stats = getCookie(cookie);
if ( !stats ) {
stats = 'on'
stats = 'on';
setCookie(cookie, stats, 10*365);
}
return stats;
}
function getStat() {
table.empty().append('<tbody>');
$j.each( eventDataStrings, function( key ) {
var th = $j('<th>').addClass('text-right').text(eventDataStrings[key]);
var tdString = ( eventData[key].length ) ? eventData[key] : 'n/a';
var td = $j('<td>').text(tdString);
var row = $j('<tr>').append(th, td);
$j('#eventStatsTable tbody').append(row);
});
}
function initPage() {
// Load the delete confirmation modal into the DOM
getDelConfirmModal();
// Load the event stats
getStat();
var stats = getEvtStatsCookie();
if ( stats != 'on' ) table.toggle(false)
if ( stats != 'on' ) table.toggle(false);
//FIXME prevent blocking...not sure what is happening or best way to unblock
if ( $j('#videoobj').length ) {
@ -1134,13 +1136,13 @@ function initPage() {
});
// enable or disable buttons based on current selection and user rights
renameBtn.prop('disabled', !canEditEvents);
archiveBtn.prop('disabled', !(!eventData.Archived && canEditEvents));
unarchiveBtn.prop('disabled', !(eventData.Archived && canEditEvents));
editBtn.prop('disabled', !canEditEvents);
exportBtn.prop('disabled', !canViewEvents);
downloadBtn.prop('disabled', !canViewEvents);
deleteBtn.prop('disabled', !canEditEvents);
renameBtn.prop('disabled', !canEdit.Events);
archiveBtn.prop('disabled', !(!eventData.Archived && canEdit.Events));
unarchiveBtn.prop('disabled', !(eventData.Archived && canEdit.Events));
editBtn.prop('disabled', !canEdit.Events);
exportBtn.prop('disabled', !canView.Events);
downloadBtn.prop('disabled', !canView.Events);
deleteBtn.prop('disabled', !canEdit.Events);
// Don't enable the back button if there is no previous zm page to go back to
backBtn.prop('disabled', !document.referrer.length);
@ -1183,7 +1185,7 @@ function initPage() {
// Manage the UNARCHIVE button
document.getElementById("unarchiveBtn").addEventListener("click", function onUnarchiveClick(evt) {
if ( ! canEditEvents ) {
if ( ! canEdit.Events ) {
enoperm();
return;
}
@ -1198,7 +1200,7 @@ function initPage() {
// Manage the EDIT button
document.getElementById("editBtn").addEventListener("click", function onEditClick(evt) {
if ( ! canEditEvents ) {
if ( ! canEdit.Events ) {
enoperm();
return;
}
@ -1239,7 +1241,7 @@ function initPage() {
// Manage the Event STATISTICS Button
document.getElementById("statsBtn").addEventListener("click", function onStatsClick(evt) {
evt.preventDefault();
var cookie = 'zmEventStats'
var cookie = 'zmEventStats';
// Toggle the visiblity of the stats table and write an appropriate cookie
if ( table.is(':visible') ) {
@ -1253,12 +1255,23 @@ function initPage() {
// Manage the DELETE button
document.getElementById("deleteBtn").addEventListener("click", function onDeleteClick(evt) {
if ( ! canEditEvents ) {
if ( ! canEdit.Events ) {
enoperm();
return;
}
evt.preventDefault();
if ( ! $j('#deleteConfirm').length ) {
// Load the delete confirmation modal into the DOM
$j.getJSON(thisUrl + '?request=modal&modal=delconfirm')
.done(function(data) {
insertModalHtml('deleteConfirm', data.html);
manageDelConfirmModalBtns();
$j('#deleteConfirm').modal('show');
})
.fail(logAjaxFail);
return;
}
$j('#deleteConfirm').modal('show');
});
}

View File

@ -42,17 +42,46 @@ var eventData = {
Id: '<?php echo $Event->Id() ?>',
Name: '<?php echo $Event->Name() ?>',
MonitorId: '<?php echo $Event->MonitorId() ?>',
MonitorName: '<?php echo validJsStr($Monitor->Name()) ?>',
Cause: '<?php echo validHtmlStr($Event->Cause()) ?>',
Width: '<?php echo $Event->Width() ?>',
Height: '<?php echo $Event->Height() ?>',
Length: '<?php echo $Event->Length() ?>',
StartDateTime: '<?php echo $Event->StartDateTime() ?>',
StartDateTimeFmt: '<?php echo strftime(STRF_FMT_DATETIME_SHORT, strtotime($Event->StartDateTime())) ?>',
EndDateTime: '<?php echo $Event->EndDateTime() ?>',
Frames: '<?php echo $Event->Frames() ?>',
MonitorName: '<?php echo validJsStr($Monitor->Name()) ?>',
AlarmFrames: '<?php echo $Event->AlarmFrames() ?>',
TotScore: '<?php echo $Event->TotScore() ?>',
AvgScore: '<?php echo $Event->AvgScore() ?>',
MaxScore: '<?php echo $Event->MaxScore() ?>',
DiskSpace: '<?php echo human_filesize($Event->DiskSpace(null)) ?>',
Storage: '<?php validHtmlStr($Event->Storage()->Name()).( $Event->SecondaryStorageId() ? ', '.validHtmlStr($Event->SecondaryStorage()->Name()) : '' ) ?>',
Archived: <?php echo $Event->Archived?'true':'false' ?>
Storage: '<?php echo validHtmlStr($Event->Storage()->Name()).( $Event->SecondaryStorageId() ? ', '.validHtmlStr($Event->SecondaryStorage()->Name()) : '' ) ?>',
ArchivedStr: '<?php echo $Event->Archived ? translate('Yes') : translate('No') ?>',
EmailedStr: '<?php echo $Event->Emailed ? translate('Yes') : translate('No') ?>',
Archived: <?php echo $Event->Archived?'true':'false' ?>,
Emailed: <?php echo $Event->Emailed?'true':'false' ?>
};
var eventDataStrings = {
Id: '<?php echo translate('EventId') ?>',
Name: '<?php echo translate('EventName') ?>',
MonitorId: '<?php echo translate('AttrMonitorId') ?>',
MonitorName: '<?php echo translate('AttrMonitorName') ?>',
Cause: '<?php echo translate('Cause') ?>',
StartDateTimeFmt: '<?php echo translate('AttrStartTime') ?>',
Length: '<?php echo translate('Duration') ?>',
Frames: '<?php echo translate('AttrFrames') ?>',
AlarmFrames: '<?php echo translate('AttrAlarmFrames') ?>',
TotScore: '<?php echo translate('AttrTotalScore') ?>',
AvgScore: '<?php echo translate('AttrAvgScore') ?>',
MaxScore: '<?php echo translate('AttrMaxScore') ?>',
DiskSpace: '<?php echo translate('DiskSpace') ?>',
Storage: '<?php echo translate('Storage') ?>',
ArchivedStr: '<?php echo translate('Archived') ?>',
EmailedStr: '<?php echo translate('Emailed') ?>'
};
var monitorUrl = '<?php echo $Event->Storage()->Server()->UrlToIndex(); ?>';
var filterQuery = '<?php echo isset($filterQuery)?validJsStr(htmlspecialchars_decode($filterQuery)):'' ?>';
@ -63,7 +92,6 @@ var rate = '<?php echo $rate ?>'; // really only used when setting up initial pl
var scale = "<?php echo $scale ?>";
var LabelFormat = "<?php echo validJsStr($Monitor->LabelFormat())?>";
var canEditEvents = <?php echo canEdit('Events')?'true':'false' ?>;
var streamTimeout = <?php echo 1000*ZM_WEB_REFRESH_STATUS ?>;
var canStreamNative = <?php echo canStreamNative()?'true':'false' ?>;

View File

@ -58,8 +58,8 @@ function processRows(rows) {
row.Id = '<a href="?view=event&amp;eid=' + eid + filterQuery + sortQuery + '&amp;page=1">' + eid + '</a>';
row.Name = '<a href="?view=event&amp;eid=' + eid + filterQuery + sortQuery + '&amp;page=1">' + row.Name + '</a>'
+ '<br/><div class="small text-nowrap text-muted">' + archived + emailed + '</div>';
if ( canEditMonitors ) row.Monitor = '<a href="?view=monitor&amp;mid=' + mid + '">' + row.Monitor + '</a>';
if ( canEditEvents ) row.Cause = '<a href="#" title="' + row.Notes + '" class="eDetailLink" data-eid="' + eid + '">' + row.Cause + '</a>';
if ( canEdit.Monitors ) row.Monitor = '<a href="?view=monitor&amp;mid=' + mid + '">' + row.Monitor + '</a>';
if ( canEdit.Events ) row.Cause = '<a href="#" title="' + row.Notes + '" class="eDetailLink" data-eid="' + eid + '">' + row.Cause + '</a>';
if ( row.Notes.indexOf('detected:') >= 0 ) {
row.Cause = row.Cause + '<a href="?view=image&amp;eid=' + eid + '&amp;fid=objdetect"><div class="small text-nowrap text-muted"><u>' + row.Notes + '</u></div></a>';
} else if ( row.Notes != 'Forced Web: ' ) {
@ -74,27 +74,6 @@ function processRows(rows) {
return rows;
}
function thumbnail_onmouseover(event) {
var img = event.target;
img.src = '';
img.src = img.getAttribute('stream_src');
}
function thumbnail_onmouseout(event) {
var img = event.target;
img.src = '';
img.src = img.getAttribute('still_src');
}
function initThumbAnimation() {
if ( WEB_ANIMATE_THUMBS ) {
$j('.colThumbnail img').each(function() {
this.addEventListener('mouseover', thumbnail_onmouseover, false);
this.addEventListener('mouseout', thumbnail_onmouseout, false);
});
}
}
// Returns the event id's of the selected rows
function getIdSelections() {
var table = $j('#eventTable');
@ -126,7 +105,7 @@ function getDelConfirmModal() {
// Manage the DELETE CONFIRMATION modal button
function manageDelConfirmModalBtns() {
document.getElementById("delConfirmBtn").addEventListener("click", function onDelConfirmClick(evt) {
if ( ! canEditEvents ) {
if ( ! canEdit.Events ) {
enoperm();
return;
}
@ -184,13 +163,13 @@ function initPage() {
function() {
selections = table.bootstrapTable('getSelections');
viewBtn.prop('disabled', !(selections.length && canViewEvents));
archiveBtn.prop('disabled', !(selections.length && canEditEvents));
unarchiveBtn.prop('disabled', !(getArchivedSelections()) && canEditEvents);
editBtn.prop('disabled', !(selections.length && canEditEvents));
exportBtn.prop('disabled', !(selections.length && canViewEvents));
downloadBtn.prop('disabled', !(selections.length && canViewEvents));
deleteBtn.prop('disabled', !(selections.length && canEditEvents));
viewBtn.prop('disabled', !(selections.length && canView.Events));
archiveBtn.prop('disabled', !(selections.length && canEdit.Events));
unarchiveBtn.prop('disabled', !(getArchivedSelections()) && canEdit.Events);
editBtn.prop('disabled', !(selections.length && canEdit.Events));
exportBtn.prop('disabled', !(selections.length && canView.Events));
downloadBtn.prop('disabled', !(selections.length && canView.Events));
deleteBtn.prop('disabled', !(selections.length && canEdit.Events));
});
// Don't enable the back button if there is no previous zm page to go back to
@ -249,7 +228,7 @@ function initPage() {
// Manage the UNARCHIVE button
document.getElementById("unarchiveBtn").addEventListener("click", function onUnarchiveClick(evt) {
if ( ! canEditEvents ) {
if ( ! canEdit.Events ) {
enoperm();
return;
}
@ -267,7 +246,7 @@ function initPage() {
// Manage the EDIT button
document.getElementById("editBtn").addEventListener("click", function onEditClick(evt) {
if ( ! canEditEvents ) {
if ( ! canEdit.Events ) {
enoperm();
return;
}
@ -313,7 +292,7 @@ function initPage() {
// Manage the DELETE button
document.getElementById("deleteBtn").addEventListener("click", function onDeleteClick(evt) {
if ( ! canEditEvents ) {
if ( ! canEdit.Events ) {
enoperm();
return;
}
@ -334,8 +313,7 @@ function initPage() {
var thumb_ndx = $j('#eventTable tr th').filter(function() {
return $j(this).text().trim() == 'Thumbnail';
}).index();
var thmbClass = WEB_ANIMATE_THUMBS ? 'colThumbnail zoom' : 'colThumbnail';
table.find("tr td:nth-child(" + (thumb_ndx+1) + ")").addClass(thmbClass);
table.find("tr td:nth-child(" + (thumb_ndx+1) + ")").addClass('colThumbnail');
});
table.bootstrapTable('resetSearch');

View File

@ -12,4 +12,3 @@ var emailedString = "<?php echo translate('Emailed') ?>";
var yesString = "<?php echo translate('Yes') ?>";
var noString = "<?php echo translate('No') ?>";
var WEB_LIST_THUMBS = <?php echo ZM_WEB_LIST_THUMBS?'true':'false' ?>;
var WEB_ANIMATE_THUMBS = <?php echo ZM_WEB_ANIMATE_THUMBS?'true':'false' ?>;

View File

@ -31,11 +31,11 @@ function startDownload(file) {
function exportProgress() {
if ( exportTimer ) {
var tickerText = $('exportProgressTicker').get('text');
var tickerText = $j('#exportProgressTicker').text();
if ( tickerText.length < 1 || tickerText.length > 4 ) {
$('exportProgressTicker').set('text', '.');
$j('#exportProgressTicker').text('.');
} else {
$('exportProgressTicker').appendText('.');
$j('#exportProgressTicker').append('.');
}
}
}
@ -43,9 +43,9 @@ function exportProgress() {
function exportResponse(respObj, respText) {
clearInterval(exportTimer);
if ( respObj.result != 'Ok' ) {
$('exportProgressTicker').set('text', respObj.message);
$j('#exportProgressTicker').text(respObj.message);
} else {
$('exportProgressTicker').set('text', exportSucceededString);
$j('#exportProgressTicker').text(exportSucceededString);
startDownload.pass(decodeURIComponent(respObj.exportFile)).delay(1500);
}
return;
@ -64,12 +64,10 @@ function exportResponse(respObj, respText) {
}
function exportEvents( ) {
var parms = 'view=event&request=event&action=export';
parms += '&'+$('contentForm').toQueryString();
var query = new Request.JSON( {
url: thisUrl,
url: '?view=event&request=event&action=export',
method: 'post',
data: parms,
data: $('contentForm').toQueryString(),
onSuccess: exportResponse
} );
query.send();

View File

@ -40,6 +40,24 @@ document.addEventListener('DOMContentLoaded', function onDCL() {
document.getElementById('scaleControl').addEventListener('change', changeScale);
});
function getStat(params) {
$j.getJSON(thisUrl + '?view=request&request=stats&raw=true', params)
.done(function(data) {
var stat = data.raw;
$j('#frameStatsTable').empty().append('<tbody>');
$j.each( statHeaderStrings, function( key ) {
var th = $j('<th>').addClass('text-right').text(statHeaderStrings[key]);
var tdString = ( stat ) ? stat[key] : 'n/a';
var td = $j('<td>').text(tdString);
var row = $j('<tr>').append(th, td);
$j('#frameStatsTable tbody').append(row);
});
})
.fail(logAjaxFail);
}
function initPage() {
var backBtn = $j('#backBtn');
@ -65,6 +83,9 @@ function initPage() {
evt.preventDefault();
window.location.href = thisUrl+'?view=stats&eid='+eid+'&fid='+fid;
});
// Load the frame stats
getStat({eid: eid, fid: fid});
}
$j(document).ready(function() {

View File

@ -13,3 +13,15 @@ var eid = <?php echo $eid ?>;
var fid = <?php echo $fid ?>;
var record_event_stats = <?php echo ZM_RECORD_EVENT_STATS ?>;
var alarmFrame = <?php echo $alarmFrame ?>;
var statHeaderStrings = {};
statHeaderStrings.ZoneName = "<?php echo translate('Zone') ?>";
statHeaderStrings.PixelDiff = "<?php echo translate('PixelDiff') ?>";
statHeaderStrings.AlarmPixels = "<?php echo translate('AlarmPx') ?>";
statHeaderStrings.FilterPixels = "<?php echo translate('FilterPx') ?>";
statHeaderStrings.BlobPixels = "<?php echo translate('BlobPx') ?>";
statHeaderStrings.Blobs = "<?php echo translate('Blobs') ?>";
statHeaderStrings.BlobSizes = "<?php echo translate('BlobSizes') ?>";
statHeaderStrings.AlarmLimits = "<?php echo translate('AlarmLimits') ?>";
statHeaderStrings.Score = "<?php echo translate('Score') ?>";

View File

@ -26,27 +26,6 @@ function processRows(rows) {
return rows;
}
function thumbnail_onmouseover(event) {
var img = event.target;
img.src = '';
img.src = img.getAttribute('full_img_src');
}
function thumbnail_onmouseout(event) {
var img = event.target;
img.src = '';
img.src = img.getAttribute('img_src');
}
function initThumbAnimation() {
if ( WEB_ANIMATE_THUMBS ) {
$j('.colThumbnail img').each(function() {
this.addEventListener('mouseover', thumbnail_onmouseover, false);
this.addEventListener('mouseout', thumbnail_onmouseout, false);
});
}
}
function processClicks(event, field, value, row, $element) {
if ( field == 'Score' ) {
window.location.assign('?view=stats&eid='+row.EventId+'&fid='+row.FrameId);
@ -118,8 +97,7 @@ function initPage() {
var thumb_ndx = $j('#framesTable tr th').filter(function() {
return $j(this).text().trim() == 'Thumbnail';
}).index();
var thmbClass = WEB_ANIMATE_THUMBS ? 'colThumbnail zoom' : 'colThumbnail';
table.find("tr td:nth-child(" + (thumb_ndx+1) + ")").addClass(thmbClass);
table.find("tr td:nth-child(" + (thumb_ndx+1) + ")").addClass('colThumbnail');
});
}

View File

@ -50,7 +50,7 @@ function deleteGroup( element ) {
}
function configureButtons( element ) {
if ( canEditGroups ) {
if ( canEdit.Groups ) {
var form = element.form;
if ( element.checked ) {
form.deleteBtn.disabled = (element.value == 0);
@ -64,7 +64,7 @@ function configModalBtns() {
console.log("No groupForm found");
return;
}
if ( !canEditGroups ) {
if ( !canEdit.Groups ) {
console.log("Cannot edit groups");
form.elements['action'].disabled = disabled;
return;

View File

@ -146,6 +146,24 @@ function initPage() {
el.oninput = window['update_estimated_ram_use'].bind(el);
});
document.querySelectorAll('select[name="newMonitor[Function]"]').forEach(function(el) {
el.onchange = function() {
$j('#function_help div').hide();
$j('#'+this.value+'Help').show();
if ( this.value == 'Monitor' || this.value == 'None' ) {
$j('#FunctionEnabled').hide();
} else {
$j('#FunctionEnabled').show();
}
if ( this.value == 'Record' || this.value == 'Nodect' ) {
$j('#FunctionDecodingEnabled').show();
} else {
$j('#FunctionDecodingEnabled').hide();
}
};
el.onchange();
});
$j('.chosen').chosen();
// Don't enable the back button if there is no previous zm page to go back to

View File

@ -62,11 +62,11 @@ function initPage() {
var NewStorageBtn = $j('#NewStorageBtn');
var NewServerBtn = $j('#NewServerBtn');
if ( canEditSystem ) enableStorageModal();
if ( canEditSystem ) enableServerModal();
if ( canEdit.System ) enableStorageModal();
if ( canEdit.System ) enableServerModal();
NewStorageBtn.prop('disabled', !canEditSystem);
NewServerBtn.prop('disabled', !canEditSystem);
NewStorageBtn.prop('disabled', !canEdit.System);
NewServerBtn.prop('disabled', !canEdit.System);
}
$j(document).ready(function() {

View File

@ -4,4 +4,3 @@ if ( restartWarning ) {
alert( "<?php echo translate('OptionRestartWarning') ?>" );
}
var canEditSystem = <?php echo canEdit('System') ? 'true' : 'false' ?>;

View File

@ -30,29 +30,28 @@ function changeDateTime(e) {
window.location = uri;
}
function datetime_change(newDate, oldData) {
if (newDate !== oldData.lastVal) {
changeDateTime();
}
}
function initPage() {
$j('#minTime').datetimepicker({
timeFormat: "HH:mm:ss",
dateFormat: "yy-mm-dd",
maxDate: +0,
constrainInput: false,
onClose: function(newDate, oldData) {
if (newDate !== oldData.lastVal) {
changeDateTime();
}
}
onClose: datetime_change
});
$j('#maxTime').datetimepicker({
timeFormat: "HH:mm:ss",
dateFormat: "yy-mm-dd",
minDate: $j('#minTime').val(),
maxDate: +0,
constrainInput: false,
onClose: function(newDate, oldData) {
if (newDate !== oldData.lastVal) {
changeDateTime();
}
}
onClose: datetime_change
});
}
// Kick everything off

View File

@ -1,3 +1,4 @@
var streamCmdTimer = null;
var streamStatus;
var auth_hash;
var alarmState = STATE_IDLE;
@ -7,23 +8,7 @@ var settingsBtn = $j('#settingsBtn');
var enableAlmBtn = $j('#enableAlmBtn');
var forceAlmBtn = $j('#forceAlmBtn');
var table = $j('#eventList');
if ( monitorType != 'WebSite' ) {
var streamCmdParms = 'view=request&request=stream&connkey='+connKey;
if ( auth_hash ) {
streamCmdParms += '&auth='+auth_hash;
}
var streamCmdReq = new Request.JSON( {
url: monitorUrl,
method: 'get',
timeout: AJAX_TIMEOUT,
link: 'chain',
onError: getStreamCmdError,
onSuccess: getStreamCmdResponse,
onFailure: getStreamCmdFailure
} );
var streamCmdTimer = null;
}
var filterQuery = '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId;
/*
This is the format of the json object sent by bootstrap-table
@ -52,17 +37,14 @@ var params =
// Called by bootstrap-table to retrieve zm event data
function ajaxRequest(params) {
// Maintain legacy behavior of sorting by Id column only
delete params.data.order;
delete params.data.limit;
params.data.sort = 'Id desc';
params.data.count = maxDisplayEvents;
params.data.id = monitorId;
if ( auth_hash ) params.data.auth = auth_hash;
// Maintain legacy behavior by statically setting these parameters
params.data.order = 'desc';
params.data.limit = maxDisplayEvents;
params.data.sort = 'Id';
$j.getJSON(thisUrl + '?view=request&request=status&entity=events', params.data)
$j.getJSON(thisUrl + '?view=request&request=events&task=query'+filterQuery, params.data)
.done(function(data) {
var rows = processRows(data.events);
var rows = processRows(data.rows);
// rearrange the result into what bootstrap-table expects
params.success({total: data.total, totalNotFiltered: data.totalNotFiltered, rows: rows});
})
@ -72,45 +54,45 @@ function ajaxRequest(params) {
function processRows(rows) {
$j.each(rows, function(ndx, row) {
var eid = row.Id;
var filterQuery = '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId;
row.Delete = '<i class="fa fa-trash text-danger"></i>';
row.Id = '<a href="?view=event&amp;eid=' + eid + filterQuery + '">' + eid + '</a>';
row.Name = '<a href="?view=event&amp;eid=' + eid + filterQuery + '">' + row.Name + '</a>';
row.Frames = '<a href="?view=frames&amp;eid=' + eid + '">' + row.Frames + '</a>';
row.AlarmFrames = '<a href="?view=frames&amp;eid=' + eid + '">' + row.AlarmFrames + '</a>';
row.MaxScore = '<a href="?view=frame&amp;eid=' + eid + '&amp;fid=0">' + row.MaxScore + '</a>';
row.Delete = '<i class="fa fa-trash text-danger"></i>';
if ( LIST_THUMBS ) row.Thumbnail = '<a href="?view=event&amp;eid=' + eid + filterQuery + '&amp;page=1">' + row.imgHtml + '</a>';
});
return rows;
}
function showEvents() {
$('ptzControls').addClass('hidden');
$('events').removeClass('hidden');
if ( $('eventsControl') ) {
$('eventsControl').addClass('hidden');
$j('#ptzControls').addClass('hidden');
$j('#events').removeClass('hidden');
if ( $j('#eventsControl') ) {
$j('#eventsControl').addClass('hidden');
}
if ( $('controlControl') ) {
$('controlControl').removeClass('hidden');
if ( $j('#controlControl') ) {
$j('#controlControl').removeClass('hidden');
}
showMode = 'events';
}
function showPtzControls() {
$('events').addClass('hidden');
$('ptzControls').removeClass('hidden');
if ( $('eventsControl') ) {
$('eventsControl').removeClass('hidden');
$j('#events').addClass('hidden');
$j('#ptzControls').removeClass('hidden');
if ( $j('#eventsControl') ) {
$j('#eventsControl').removeClass('hidden');
}
if ( $('controlControl') ) {
$('controlControl').addClass('hidden');
if ( $j('#controlControl') ) {
$j('#controlControl').addClass('hidden');
}
showMode = 'control';
}
function changeScale() {
var scale = $('scale').get('value');
var scale = $j('#scale').val();
var newWidth;
var newHeight;
if ( scale == '0' || scale == 'auto' ) {
@ -126,13 +108,14 @@ function changeScale() {
Cookie.write('zmWatchScale'+monitorId, scale, {duration: 10*365, samesite: 'strict'});
/*Stream could be an applet so can't use moo tools*/
var streamImg = $('liveStream'+monitorId);
var streamImg = $j('#liveStream'+monitorId);
if ( streamImg ) {
streamImg.style.width = newWidth + 'px';
streamImg.style.height = newHeight + 'px';
var oldSrc = streamImg.attr('src');
var newSrc = oldSrc.replace(/scale=\d+/i, 'scale='+(scale== 'auto' ? autoScale : scale));
streamImg.src = streamImg.src.replace(/scale=\d+/i, 'scale='+(scale== 'auto' ? autoScale : scale));
streamImg.width( newWidth );
streamImg.height( newHeight );
streamImg.src = newSrc;
} else {
console.error('No element found for liveStream'+monitorId);
}
@ -147,11 +130,11 @@ function setAlarmState( currentAlarmState ) {
} else if ( alarmState == STATE_ALERT ) {
stateClass = 'alert';
}
$('stateValue').set('text', stateStrings[alarmState]);
$j('#stateValue').text(stateStrings[alarmState]);
if ( stateClass ) {
$('stateValue').setProperty('class', stateClass);
$j('#stateValue').addClass(stateClass);
} else {
$('stateValue').removeProperty('class');
$j('#stateValue').removeClass();
}
var isAlarmed = ( alarmState == STATE_ALARM || alarmState == STATE_ALERT );
@ -161,12 +144,13 @@ function setAlarmState( currentAlarmState ) {
var oldAlarm = ( !isAlarmed && wasAlarmed );
if ( newAlarm ) {
table.bootstrapTable('refresh');
if ( SOUND_ON_ALARM ) {
// Enable the alarm sound
if ( !canPlayPauseAudio ) {
$('alarmSound').removeClass('hidden');
$j('#alarmSound').removeClass('hidden');
} else {
$('MediaPlayer').Play();
$j('#MediaPlayer').trigger('play');
}
}
if ( POPUP_ON_ALARM ) {
@ -174,15 +158,15 @@ function setAlarmState( currentAlarmState ) {
}
}
if ( oldAlarm ) { // done with an event do a refresh
table.bootstrapTable('refresh');
if ( SOUND_ON_ALARM ) {
// Disable alarm sound
if ( !canPlayPauseAudio ) {
$('alarmSound').addClass('hidden');
$j('#alarmSound').addClass('hidden');
} else {
$('MediaPlayer').Stop();
$j('#MediaPlayer').trigger('pause');
}
}
table.bootstrapTable('refresh');
}
lastAlarmState = alarmState;
@ -194,10 +178,6 @@ function getStreamCmdError(text, error) {
window.location.reload();
}
function getStreamCmdFailure(xhr) {
console.log(xhr);
}
function getStreamCmdResponse(respObj, respText) {
watchdogOk('stream');
if ( streamCmdTimer ) {
@ -207,35 +187,36 @@ function getStreamCmdResponse(respObj, respText) {
// The get status command can get backed up, in which case we won't be able to get the semaphore and will exit.
if ( respObj.status ) {
streamStatus = respObj.status;
$('fpsValue').set('text', streamStatus.fps);
$j('#fpsValue').text(streamStatus.fps);
setAlarmState(streamStatus.state);
$('levelValue').set('text', streamStatus.level);
$j('#levelValue').text(streamStatus.level);
var newClass = 'ok';
if ( streamStatus.level > 95 ) {
$('levelValue').className = 'alarm';
newClass = 'alarm';
} else if ( streamStatus.level > 80 ) {
$('levelValue').className = 'alert';
} else {
$('levelValue').className = 'ok';
newClass = 'alert';
}
$j('#levelValue').removeClass();
$j('#levelValue').addClass(newClass);
var delayString = secsToTime(streamStatus.delay);
if ( streamStatus.paused == true ) {
$('modeValue').set('text', 'Paused');
$('rate').addClass('hidden');
$('delayValue').set('text', delayString);
$('delay').removeClass('hidden');
$('level').removeClass('hidden');
$j('#modeValue').text('Paused');
$j('#rate').addClass('hidden');
$j('#delayValue').text(delayString);
$j('#delay').removeClass('hidden');
$j('#level').removeClass('hidden');
streamCmdPause(false);
} else if ( streamStatus.delayed == true ) {
$('modeValue').set('text', 'Replay');
$('rateValue').set('text', streamStatus.rate);
$('rate').removeClass('hidden');
$('delayValue').set('text', delayString);
$('delay').removeClass('hidden');
$('level').removeClass('hidden');
$j('#modeValue').text('Replay');
$j('#rateValue').text(streamStatus.rate);
$j('#rate').removeClass('hidden');
$j('#delayValue').text(delayString);
$j('#delay').removeClass('hidden');
$j('#level').removeClass('hidden');
if ( streamStatus.rate == 1 ) {
streamCmdPlay(false);
} else if ( streamStatus.rate > 0 ) {
@ -252,21 +233,21 @@ function getStreamCmdResponse(respObj, respText) {
}
} // rate
} else {
$('modeValue').set( 'text', 'Live' );
$('rate').addClass( 'hidden' );
$('delay').addClass( 'hidden' );
$('level').addClass( 'hidden' );
$j('#modeValue').text( 'Live' );
$j('#rate').addClass( 'hidden' );
$j('#delay').addClass( 'hidden' );
$j('#level').addClass( 'hidden' );
streamCmdPlay(false);
} // end if paused or delayed
$('zoomValue').set('text', streamStatus.zoom);
$j('zoomValue').text(streamStatus.zoom);
if ( streamStatus.zoom == '1.0' ) {
setButtonState('zoomOutBtn', 'unavail');
} else {
setButtonState('zoomOutBtn', 'inactive');
}
if ( canEditMonitors ) {
if ( canEdit.Monitors ) {
if ( streamStatus.enabled ) {
enableAlmBtn.addClass('disabled');
enableAlmBtn.prop('title', disableAlarmsStr);
@ -284,19 +265,18 @@ function getStreamCmdResponse(respObj, respText) {
forceAlmBtn.prop('disabled', true);
}
enableAlmBtn.prop('disabled', false);
} // end if canEditMonitors
} // end if canEdit.Monitors
if ( streamStatus.auth ) {
auth_hash = streamStatus.auth;
// Try to reload the image stream.
var streamImg = $('liveStream');
var streamImg = $j('#liveStream'+monitorId);
if ( streamImg ) {
streamImg.src = streamImg.src.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
var oldSrc = streamImg.attr('src');
var newSrc = oldSrc.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
streamImg.src = newSrc;
}
streamCmdParms = streamCmdParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
statusCmdParms = statusCmdParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
table.bootstrapTable('refresh');
controlParms = controlParms.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
} // end if have a new auth hash
} // end if respObj.status
} else {
@ -305,9 +285,12 @@ function getStreamCmdResponse(respObj, respText) {
// If it's an auth error, we should reload the whole page.
window.location.reload();
if ( 0 ) {
var streamImg = $('liveStream'+monitorId);
var streamImg = $j('#liveStream'+monitorId);
if ( streamImg ) {
streamImg.src = streamImg.src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) ));
var oldSrc = streamImg.attr('src');
var newSrc = oldSrc.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) ));
streamImg.src = newSrc;
console.log('Changing livestream src to ' + streamImg.src);
} else {
console.log('Unable to find streamImg liveStream');
@ -333,7 +316,10 @@ function streamCmdPause( action ) {
setButtonState('fastRevBtn', 'inactive');
}
if ( action ) {
streamCmdReq.send(streamCmdParms+"&command="+CMD_PAUSE);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = CMD_PAUSE;
streamCmdReq(data);
}
}
@ -358,10 +344,21 @@ function streamCmdPlay( action ) {
}
}
if ( action ) {
streamCmdReq.send(streamCmdParms+"&command="+CMD_PLAY);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = CMD_PLAY;
streamCmdReq(data);
}
}
function streamCmdReq(data) {
$j.getJSON(thisUrl + '?view=request&request=stream&connkey='+connKey, data)
.done(getStreamCmdResponse)
.fail(getStreamCmdError);
streamCmdTimer = null;
}
function streamCmdStop( action ) {
setButtonState('pauseBtn', 'inactive');
setButtonState('playBtn', 'unavail');
@ -373,7 +370,10 @@ function streamCmdStop( action ) {
setButtonState('fastRevBtn', 'unavail');
}
if ( action ) {
streamCmdReq.send(streamCmdParms+"&command="+CMD_STOP);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = CMD_STOP;
streamCmdReq(data);
}
setButtonState('stopBtn', 'unavail');
setButtonState('playBtn', 'active');
@ -390,7 +390,10 @@ function streamCmdFastFwd( action ) {
setButtonState('fastRevBtn', 'inactive');
}
if ( action ) {
streamCmdReq.send(streamCmdParms+"&command="+CMD_FASTFWD);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = CMD_FASTFWD;
streamCmdReq(data);
}
}
@ -405,7 +408,10 @@ function streamCmdSlowFwd( action ) {
setButtonState('fastRevBtn', 'inactive');
}
if ( action ) {
streamCmdReq.send(streamCmdParms+"&command="+CMD_SLOWFWD);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = CMD_SLOWFWD;
streamCmdReq(data);
}
setButtonState('pauseBtn', 'active');
if ( monitorStreamReplayBuffer ) {
@ -424,7 +430,10 @@ function streamCmdSlowRev( action ) {
setButtonState('fastRevBtn', 'inactive');
}
if ( action ) {
streamCmdReq.send(streamCmdParms+"&command="+CMD_SLOWREV);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = CMD_SLOWREV;
streamCmdReq(data);
}
setButtonState('pauseBtn', 'active');
if ( monitorStreamReplayBuffer ) {
@ -443,43 +452,51 @@ function streamCmdFastRev( action ) {
setButtonState('fastRevBtn', 'inactive');
}
if ( action ) {
streamCmdReq.send(streamCmdParms+"&command="+CMD_FASTREV);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = CMD_FASTREV;
streamCmdReq(data);
}
}
function streamCmdZoomIn( x, y ) {
streamCmdReq.send(streamCmdParms+"&command="+CMD_ZOOMIN+"&x="+x+"&y="+y);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.x = x;
data.y = y;
data.command = CMD_ZOOMIN;
streamCmdReq(data);
}
function streamCmdZoomOut() {
streamCmdReq.send(streamCmdParms+"&command="+CMD_ZOOMOUT);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = CMD_ZOOMOUT;
streamCmdReq(data);
}
function streamCmdScale( scale ) {
streamCmdReq.send(streamCmdParms+"&command="+CMD_SCALE+"&scale="+scale);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = CMD_SCALE;
data.scale = scale;
streamCmdReq(data);
}
function streamCmdPan( x, y ) {
streamCmdReq.send(streamCmdParms+"&command="+CMD_PAN+"&x="+x+"&y="+y);
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.x = x;
data.y = y;
data.command = CMD_PAN;
streamCmdReq(data);
}
function streamCmdQuery() {
streamCmdReq.send(streamCmdParms+"&command="+CMD_QUERY);
}
if ( monitorType != 'WebSite' ) {
var statusCmdParms = "view=request&request=status&entity=monitor&id="+monitorId+"&element[]=Status&element[]=FrameRate";
if ( auth_hash ) {
statusCmdParms += '&auth='+auth_hash;
}
var statusCmdReq = new Request.JSON( {
url: monitorUrl,
method: 'get',
timeout: AJAX_TIMEOUT,
link: 'cancel',
onSuccess: getStatusCmdResponse
} );
var statusCmdTimer = null;
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = CMD_QUERY;
streamCmdReq(data);
}
function getStatusCmdResponse(respObj, respText) {
@ -489,7 +506,7 @@ function getStatusCmdResponse(respObj, respText) {
}
if ( respObj.result == 'Ok' ) {
$('fpsValue').set('text', respObj.monitor.FrameRate);
$j('#fpsValue').text(respObj.monitor.FrameRate);
setAlarmState(respObj.monitor.Status);
} else {
checkStreamForErrors('getStatusCmdResponse', respObj);
@ -503,23 +520,23 @@ function getStatusCmdResponse(respObj, respText) {
}
function statusCmdQuery() {
statusCmdReq.send(statusCmdParms);
$j.getJSON(thisUrl + '?view=request&request=status&entity=monitor&element[]=Status&element[]=FrameRate&id='+monitorId)
.done(getStatusCmdResponse)
.fail(logAjaxFail);
streamCmdTimer = null;
}
if ( monitorType != 'WebSite' ) {
var alarmCmdParms = 'view=request&request=alarm&id='+monitorId;
if ( auth_hash ) {
alarmCmdParms += '&auth='+auth_hash;
function alarmCmdReq(data) {
$j.getJSON(thisUrl + '?view=request&request=alarm&id='+monitorId, data)
.done(getAlarmCmdResponse)
.fail(function(jqxhr, textStatus, error) {
if (textstatus === "timeout") {
streamCmdQuery();
} else {
logAjaxFail(jqxhr, textStatus, error);
}
var alarmCmdReq = new Request.JSON( {
url: monitorUrl,
method: 'get',
timeout: AJAX_TIMEOUT,
link: 'cancel',
onSuccess: getAlarmCmdResponse,
onTimeout: streamCmdQuery
});
var alarmCmdFirst = true;
}
function getAlarmCmdResponse(respObj, respText) {
@ -527,11 +544,17 @@ function getAlarmCmdResponse(respObj, respText) {
}
function cmdDisableAlarms() {
alarmCmdReq.send(alarmCmdParms+"&command=disableAlarms");
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = 'disableAlarms';
alarmCmdReq(data);
}
function cmdEnableAlarms() {
alarmCmdReq.send(alarmCmdParms+"&command=enableAlarms");
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = 'enableAlarms';
alarmCmdReq(data);
}
function cmdAlarm() {
@ -543,17 +566,19 @@ function cmdAlarm() {
}
function cmdForceAlarm() {
alarmCmdReq.send(alarmCmdParms+"&command=forceAlarm");
if ( window.event ) {
window.event.preventDefault();
}
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = 'forceAlarm';
alarmCmdReq(data);
if ( window.event ) window.event.preventDefault();
}
function cmdCancelForcedAlarm() {
alarmCmdReq.send(alarmCmdParms+"&command=cancelForcedAlarm");
if ( window.event ) {
window.event.preventDefault();
}
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.command = 'cancelForcedAlarm';
alarmCmdReq(data);
if ( window.event ) window.event.preventDefault();
return false;
}
@ -565,18 +590,10 @@ function cmdForce() {
}
}
if ( monitorType != 'WebSite' ) {
var controlParms = 'view=request&request=control&id='+monitorId;
if ( auth_hash ) {
controlParms += '&auth='+auth_hash;
}
var controlReq = new Request.JSON( {
url: monitorUrl,
method: 'post',
timeout: AJAX_TIMEOUT,
link: 'cancel',
onSuccess: getControlResponse
} );
function controlReq(data) {
$j.getJSON(thisUrl + '?view=request&request=control&id='+monitorId, data)
.done(getControlResponse)
.fail(logAjaxFail);
}
function getControlResponse(respObj, respText) {
@ -595,60 +612,73 @@ function controlCmd(event) {
xtell = button.getAttribute('data-xtell');
ytell = button.getAttribute('data-ytell');
var locParms = '';
var data = {};
if ( event && (xtell || ytell) ) {
var target = event.target;
var coords = $(target).getCoordinates();
var offset = $j(target).offset();
var width = $j(target).width();
var height = $j(target).height();
var x = event.pageX - coords.left;
var y = event.pageY - coords.top;
var x = event.pageX - offset.left;
var y = event.pageY - offset.top;
if ( xtell ) {
var xge = parseInt((x*100)/coords.width);
var xge = parseInt((x*100)/width);
if ( xtell == -1 ) {
xge = 100 - xge;
} else if ( xtell == 2 ) {
xge = 2*(50 - xge);
}
locParms += '&xge='+xge;
data.xge = xge;
}
if ( ytell ) {
var yge = parseInt((y*100)/coords.height);
var yge = parseInt((y*100)/height);
if ( ytell == -1 ) {
yge = 100 - yge;
} else if ( ytell == 2 ) {
yge = 2*(50 - yge);
}
locParms += '&yge='+yge;
data.yge = yge;
}
}
controlReq.send(controlParms+"&control="+control+locParms);
if ( auth_hash ) data.auth = auth_hash;
data.control = control;
controlReq(data);
if ( streamMode == 'single' ) {
fetchImage.pass($('imageFeed').getElement('img')).delay(1000);
setTimeout(fetchImage, 1000, $j('#imageFeed img'));
}
}
function controlCmdImage( x, y ) {
var imageControlParms = controlParms;
imageControlParms += '&scale='+scale;
imageControlParms += '&control='+imageControlMode;
var data = {};
if ( auth_hash ) data.auth = auth_hash;
data.scale = scale;
data.control = imageControlMode;
data.x = x;
data.y = y;
controlReq(data);
controlReq.send( imageControlParms+"&x="+x+"&y="+y );
if ( streamMode == 'single' ) {
fetchImage.pass( $('imageFeed').getElement('img') ).delay( 1000 );
setTimeout(fetchImage, 1000, $j('#imageFeed img'));
}
}
function fetchImage( streamImage ) {
streamImage.src = streamImage.src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) ));
streamImage.attr('src', streamImage.attr('src').replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) )));
}
function handleClick( event ) {
var $target = $(event.target);
var scaleX = parseInt(monitorWidth / $target.getWidth());
var scaleY = parseInt(monitorHeight / $target.getHeight());
var x = (event.page.x - $target.getLeft()) * scaleX;
var y = (event.page.y - $target.getTop()) * scaleY;
var target = event.target;
var width = $j(target).width();
var height = $j(target).height();
var scaleX = parseInt(monitorWidth / width);
var scaleY = parseInt(monitorHeight / height);
var x = (event.page.x - target.getLeft()) * scaleX;
var y = (event.page.y - target.getTop()) * scaleY;
if ( showMode == 'events' || !imageControlMode ) {
if ( event.shift ) {
@ -665,11 +695,11 @@ function handleClick( event ) {
function appletRefresh() {
if ( streamStatus && (!streamStatus.paused && !streamStatus.delayed) ) {
var streamImg = $('liveStream'+monitorId);
var streamImg = $j('#liveStream'+monitorId);
if ( streamImg ) {
var parent = streamImg.getParent();
streamImg.dispose();
streamImg.inject( parent );
var parent = streamImg.parent();
streamImg.remove();
streamImg.append( parent );
} else {
console.error("Nothing found for liveStream"+monitorId);
}
@ -711,15 +741,9 @@ function reloadWebSite() {
}
function updatePresetLabels() {
var form = $('ctrlPresetForm');
var preset_ddm = form.elements['preset'];
var lblNdx = $j( '#ctrlPresetForm option:selected' ).val();
var presetIndex = preset_ddm[preset_ddm.selectedIndex].value;
if ( labels[presetIndex] ) {
form.newLabel.value = labels[presetIndex];
} else {
form.newLabel.value = '';
}
$j('#newLabel').val(labels[lblNdx]);
}
function getCtrlPresetModal() {
@ -767,7 +791,7 @@ function processClicks(event, field, value, row, $element) {
// Manage the DELETE CONFIRMATION modal button
function manageDelConfirmModalBtns() {
document.getElementById("delConfirmBtn").addEventListener("click", function onDelConfirmClick(evt) {
if ( ! canEditEvents ) {
if ( ! canEdit.Events ) {
enoperm();
return;
}
@ -790,7 +814,7 @@ function manageDelConfirmModalBtns() {
}
function initPage() {
if ( canViewControl ) {
if ( canView.Control ) {
// Load the PTZ Preset modal into the DOM
if ( monitorControllable ) getCtrlPresetModal();
// Load the settings modal into the DOM
@ -800,25 +824,25 @@ function initPage() {
if ( monitorType != 'WebSite' ) {
if ( streamMode == 'single' ) {
statusCmdTimer = statusCmdQuery.delay( (Math.random()+0.1)*statusRefreshTimeout );
watchdogCheck.pass('status').periodical(statusRefreshTimeout*2);
setInterval(watchdogCheck, statusRefreshTimeout*2, 'status');
} else {
streamCmdTimer = streamCmdQuery.delay( (Math.random()+0.1)*statusRefreshTimeout );
watchdogCheck.pass('stream').periodical(statusRefreshTimeout*2);
setInterval(watchdogCheck, statusRefreshTimeout*2, 'stream');
}
if ( canStreamNative || (streamMode == 'single') ) {
var streamImg = $('imageFeed').getElement('img');
var streamImg = $j('#imageFeed img');
if ( !streamImg ) {
streamImg = $('imageFeed').getElement('object');
streamImg = $j('#imageFeed object');
}
if ( !streamImg ) {
console.error('No streamImg found for imageFeed');
} else {
if ( streamMode == 'single' ) {
streamImg.addEvent('click', fetchImage.pass(streamImg));
fetchImage.pass(streamImg).periodical(imageRefreshTimeout);
streamImg.click(streamImg, fetchImage);
setInterval(fetchImage, imageRefreshTimeout, $j('#imageFeed img'));
} else {
streamImg.addEvent('click', function(event) {
streamImg.click(function(event) {
handleClick(event);
});
}
@ -861,7 +885,7 @@ function initPage() {
});
// Only enable the settings button for local cameras
settingsBtn.prop('disabled', !canViewControl);
settingsBtn.prop('disabled', !canView.Control);
if ( monitorType != 'Local' ) settingsBtn.hide();
// Init the bootstrap-table
@ -874,6 +898,17 @@ function initPage() {
// Take appropriate action when the user clicks on a cell
table.on('click-cell.bs.table', processClicks);
// Some toolbar events break the thumbnail animation, so re-init eventlistener
table.on('all.bs.table', initThumbAnimation);
// Update table links each time after new data is loaded
table.on('post-body.bs.table', function(data) {
var thumb_ndx = $j('#eventList tr th').filter(function() {
return $j(this).text().trim() == 'Thumbnail';
}).index();
table.find("tr td:nth-child(" + (thumb_ndx+1) + ")").addClass('colThumbnail');
});
} // initPage
// Kick everything off

View File

@ -50,6 +50,7 @@ var SCALE_BASE = <?php echo SCALE_BASE ?>;
var SOUND_ON_ALARM = <?php echo ZM_WEB_SOUND_ON_ALARM ?>;
var POPUP_ON_ALARM = <?php echo ZM_WEB_POPUP_ON_ALARM ?>;
var LIST_THUMBS = <?php echo ZM_WEB_LIST_THUMBS?'true':'false' ?>;
var streamMode = "<?php echo $streamMode ?>";
var showMode = "<?php echo ($showPtzControls && !empty($control))?"control":"events" ?>";
@ -72,9 +73,7 @@ var statusRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_STATUS ?>;
var eventsRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_EVENTS ?>;
var imageRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_IMAGE ?>;
var canEditMonitors = <?php echo canEdit( 'Monitors' )?'true':'false' ?>;
var canStreamNative = <?php echo canStreamNative()?'true':'false' ?>;
var canViewControl = <?php echo canView( 'Control' )?'true':'false' ?>;
var canPlayPauseAudio = Browser.ie;

View File

@ -128,7 +128,6 @@ var streamSrc = "<?php echo preg_replace( '/&amp;/', '&', $streamSrc ) ?>";
var statusRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_STATUS ?>;
var imageRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_IMAGE ?>;
var canEditMonitors = <?php echo canEdit( 'Monitors' )?'true':'false' ?>;
var canStreamNative = <?php echo canStreamNative()?'true':'false' ?>;
var canPlayPauseAudio = Browser.ie;

View File

@ -27,7 +27,7 @@ xhtmlHeaders(__FILE__, translate('SystemLog'));
?>
<body>
<?php echo getNavBarHTML() ?>
<div id="page" class="px-3">
<div id="page" class="px-3 table-responsive-sm">
<div id="logSummary" class="text-center">
<?php echo translate('State') ?>:&nbsp;<span id="logState"></span>&nbsp;-&nbsp;
@ -62,7 +62,6 @@ xhtmlHeaders(__FILE__, translate('SystemLog'));
data-toolbar="#toolbar"
data-show-fullscreen="true"
data-maintain-meta-data="true"
data-mobile-responsive="true"
data-buttons-class="btn btn-normal"
data-show-jump-to="true"
data-auto-refresh="true"

View File

@ -369,8 +369,10 @@ $fastblendopts_alarm = array(
);
$label_size = array(
1 => translate('Default'),
2 => translate('Large'),
1 => translate('Small'),
2 => translate('Default'),
3 => translate('Large'),
4 => translate('Extra Large'),
);
$codecs = array(
@ -468,7 +470,7 @@ if ( canEdit('Monitors') ) {
<div class="tab-content" id="pills-tabContent">
<?php
foreach ( $tabs as $name=>$value ) {
echo '<div id="pills-'.$name.'" class="tab-pane fade'.($name==$tab ? ' show active' : '').'" role="tabpanel" area-labelledby="'.$name.'-tab">';
echo '<div id="pills-'.$name.'" class="tab-pane fade'.($name==$tab ? ' show active' : '').'" role="tabpanel" aria-labelledby="'.$name.'-tab">';
?>
<table class="major">
<tbody>
@ -500,6 +502,9 @@ switch ( $name ) {
<td class="text-right pr-3"><?php echo translate('SourceType') ?></td>
<td><?php echo htmlSelect('newMonitor[Type]', $sourceTypes, $monitor->Type()); ?></td>
</tr>
<?php
if ( $monitor->Type() != 'WebSite' ) {
?>
<tr>
<td class="text-right pr-3"><?php echo translate('Function') ?></td>
<td>
@ -510,15 +515,37 @@ switch ( $name ) {
}
echo htmlSelect('newMonitor[Function]', $function_options, $monitor->Function());
?>
<div id="function_help">
<?php
foreach ( ZM\getMonitorFunctionTypes() as $fn => $translated ) {
if ( isset($OLANG['FUNCTION_'.strtoupper($fn)]) ) {
echo '<div class="form-text" id="'.$fn.'Help">'.$OLANG['FUNCTION_'.strtoupper($fn)]['Help'].'</div>';
}
}
?>
</div>
</td>
</tr>
<tr>
<td class="text-right pr-3"><?php echo translate('Enabled') ?></td>
<td><input type="checkbox" name="newMonitor[Enabled]" value="1"<?php echo $monitor->Enabled() ? ' checked="checked"' : '' ?>/></td>
</tr>
<tr id="FunctionEnabled">
<td class="text-right pr-3"><?php echo translate('Analysis Enabled') ?></td>
<td><input type="checkbox" name="newMonitor[Enabled]" value="1"<?php echo $monitor->Enabled() ? ' checked="checked"' : '' ?>/>
<?php
if ( $monitor->Type() != 'WebSite' ) {
if ( isset($OLANG['FUNCTION_ANALYSIS_ENABLED']) ) {
echo '<div class="form-text">'.$OLANG['FUNCTION_ANALYSIS_ENABLED']['Help'].'</div>';
}
?>
</td>
</tr>
<tr id="FunctionDecodingEnabled">
<td class="text-right pr-3"><?php echo translate('Decoding Enabled') ?></td>
<td><input type="checkbox" name="newMonitor[DecodingEnabled]" value="1"<?php echo $monitor->DecodingEnabled() ? ' checked="checked"' : '' ?>/>
<?php
if ( isset($OLANG['FUNCTION_DECODING_ENABLED']) ) {
echo '<div class="form-text">'.$OLANG['FUNCTION_DECODING_ENABLED']['Help'].'</div>';
}
?>
</td>
</tr>
<tr>
<td class="text-right pr-3"><?php echo translate('LinkedMonitors'); echo makeHelpLink('OPTIONS_LINKED_MONITORS') ?></td>
<td>
@ -823,11 +850,14 @@ include('_monitor_source_nvsocket.php');
'320x240'=>'320x240',
'320x200'=>'320x200',
'352x240'=>'352x240 CIF',
'352x480'=>'352x480',
'640x480'=>'640x480',
'640x400'=>'640x400',
'704x240'=>'704x240 2CIF',
'704x480'=>'704x480 4CIF',
'720x480'=>'720x480 D1',
'704x576'=>'704x576 D1 PAL',
'720x480'=>'720x480 Full D1 NTSC',
'720x576'=>'720x576 Full D1 PAL',
'1280x720'=>'1280x720 720p',
'1280x800'=>'1280x800',
'1280x960'=>'1280x960 960p',

View File

@ -38,30 +38,24 @@ if ( isset($_REQUEST['maxTime']) ) {
$maxTime = strftime('%FT%T',time() - 3600);
}
$filter = array(
'Query' => array(
'terms' => array(
array('attr'=>'StartDateTime', 'op'=>'>=', 'val'=>$minTime, 'obr'=>'1'),
array('attr'=>'StartDateTime', 'op'=>'<=', 'val'=>$maxTime, 'cnj'=>'and', 'cbr'=>'1'),
)
),
);
$filter = new ZM\Filter();
$filter->addTerm(array('attr'=>'StartDateTime', 'op'=>'>=', 'val'=>$minTime, 'obr'=>'1'));
$filter->addTerm(array('attr'=>'StartDateTime', 'op'=>'<=', 'val'=>$maxTime, 'cnj'=>'and', 'cbr'=>'1'));
if ( count($selected_monitor_ids) ) {
$filter['Query']['terms'][] = (array('attr'=>'MonitorId', 'op'=>'IN', 'val'=>implode(',', $selected_monitor_ids), 'cnj'=>'and'));
$filter->addTerm(array('attr'=>'MonitorId', 'op'=>'IN', 'val'=>implode(',', $selected_monitor_ids), 'cnj'=>'and'));
} else if ( ( $group_id != 0 || isset($_SESSION['ServerId']) || isset($_SESSION['StorageId']) || isset($_SESSION['Status']) ) ) {
# this should be redundant
for ( $i=0; $i < count($displayMonitors); $i++ ) {
if ( $i == 0 ) {
$filter['Query']['terms'][] = array('attr'=>'MonitorId', 'op'=>'=', 'val'=>$displayMonitors[$i]['Id'], 'cnj'=>'and', 'obr'=>'1');
$filter->addTerm(array('attr'=>'MonitorId', 'op'=>'=', 'val'=>$displayMonitors[$i]['Id'], 'cnj'=>'and', 'obr'=>'1'));
} else if ( $i == count($displayMonitors)-1 ) {
$filter['Query']['terms'][] = array('attr'=>'MonitorId', 'op'=>'=', 'val'=>$displayMonitors[$i]['Id'], 'cnj'=>'or', 'cbr'=>'1');
$filter->addTerm(array('attr'=>'MonitorId', 'op'=>'=', 'val'=>$displayMonitors[$i]['Id'], 'cnj'=>'or', 'cbr'=>'1'));
} else {
$filter['Query']['terms'][] = array('attr'=>'MonitorId', 'op'=>'=', 'val'=>$displayMonitors[$i]['Id'], 'cnj'=>'or');
$filter->addTerm(array('attr'=>'MonitorId', 'op'=>'=', 'val'=>$displayMonitors[$i]['Id'], 'cnj'=>'or'));
}
}
}
parseFilter($filter);
$filterQuery = $filter['query'];
$filterQuery = $filter->querystring();
ZM\Debug($filterQuery);
$eventsSql = 'SELECT *,
@ -113,17 +107,16 @@ while ( $event = $result->fetch(PDO::FETCH_ASSOC) ) {
?>
<body>
<?php echo $navbar ?>
<form name="monitorForm" method="get" action="?">
<input type="hidden" name="view" value="<?php echo $view ?>"/>
<input type="hidden" name="action" value=""/>
<?php echo $navbar ?>
<div class="filterBar">
<?php echo $filterbar ?>
<div id="DateTimeDiv">
<label>Event Start Time</label>
<input type="text" name="minTime" id="minTime" value="<?php echo preg_replace('/T/', ' ', $minTime) ?>" oninput="this.form.submit();"/> to
<input type="text" name="maxTime" id="maxTime" value="<?php echo preg_replace('/T/', ' ', $maxTime) ?>" oninput="this.form.submit();"/>
<input type="text" name="minTime" id="minTime" value="<?php echo preg_replace('/T/', ' ', $minTime) ?>"/> to
<input type="text" name="maxTime" id="maxTime" value="<?php echo preg_replace('/T/', ' ', $maxTime) ?>"/>
</div>
</div><!--FilterBar-->
@ -150,12 +143,7 @@ for ( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
$Monitor = new ZM\Monitor($monitor);
$montagereview_link = '?view=montagereview&live=0&MonitorId='.$monitor['Id'].'&minTime='.$minTime.'&maxTime='.$maxTime;
$monitor_filter = addFilterTerm(
$filter,
count($filter['Query']['terms']),
array('cnj'=>'and', 'attr'=>'MonitorId', 'op'=>'=', 'val'=>$monitor['Id'])
);
parseFilter($monitor_filter);
$monitor_filter = $filter->addTerm(array('cnj'=>'and', 'attr'=>'MonitorId', 'op'=>'=', 'val'=>$monitor['Id']));
if ( isset($EventsByMonitor[$Monitor->Id()]) ) {
$EventCounts = $EventsByMonitor[$Monitor->Id()];
@ -175,24 +163,12 @@ for ( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
}
if ( count($FileMissing) ) {
$FileMissing_filter = array(
'Query' => array(
'terms' => array(
array('attr'=>'Id', 'op'=>'IN', 'val'=>implode(',', array_map(function($Event){return $Event->Id();}, $FileMissing)))
)
)
);
parseFilter($FileMissing_filter);
$FileMissing_filter = new ZM\Filter();
$FileMissing_filter->addTerm(array('attr'=>'Id', 'op'=>'IN', 'val'=>implode(',', array_map(function($Event){return $Event->Id();}, $FileMissing))));
}
if ( count($ZeroSize) ) {
$ZeroSize_filter = array(
'Query' => array(
'terms' => array(
array('attr'=>'Id', 'op'=>'IN', 'val'=>implode(',', array_map(function($Event){return $Event->Id();}, $ZeroSize)))
)
)
);
parseFilter($ZeroSize_filter);
$ZeroSize_filter = new ZM\Filter();
$ZeroSize_filter->addTerm(array('attr'=>'Id', 'op'=>'IN', 'val'=>implode(',', array_map(function($Event){return $Event->Id();}, $ZeroSize))));
}
?>
<tr id="<?php echo 'monitor_id-'.$monitor['Id'] ?>" title="<?php echo $monitor['Id'] ?>">
@ -202,24 +178,27 @@ for ( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
<div class="small text-nowrap text-muted">
<?php echo implode('<br/>',
array_map(function($group_id){
$Group = new ZM\Group($group_id);
$Group = ZM\Group::find_one(array('Id'=>$group_id));
if ( $Group ) {
$Groups = $Group->Parents();
array_push( $Groups, $Group );
return implode(' &gt; ', array_map(function($Group){ return '<a href="?view=montagereview&GroupId='.$Group->Id().'">'.$Group->Name().'</a>'; }, $Groups ));
}
return implode(' &gt; ', array_map(function($Group){ return '<a href="?view=montagereview&amp;GroupId='.$Group->Id().'">'.validHtmlStr($Group->Name()).'</a>'; }, $Groups ));
}, $Monitor->GroupIds()));
?>
</div></td>
<td class="colServer"><?php echo validHtmlStr($Monitor->Server()->Name())?></td>
<td class="colEvents"><a href="?view=<?php echo ZM_WEB_EVENTS_VIEW ?>&amp;page=1<?php echo $monitor_filter['query'] ?>"><?php echo isset($EventsByMonitor[$Monitor->Id()])?count($EventsByMonitor[$Monitor->Id()]['Events']):0 ?></a></td>
<td class="colEvents"><a href="?view=<?php echo ZM_WEB_EVENTS_VIEW ?>&amp;page=1<?php echo $monitor_filter->querystring() ?>"><?php echo isset($EventsByMonitor[$Monitor->Id()])?count($EventsByMonitor[$Monitor->Id()]['Events']):0 ?></a></td>
<td class="colFirstEvent"><?php echo $FirstEvent ? $FirstEvent->link_to($FirstEvent->Id().' at '.$FirstEvent->StartDateTime()) : 'none'?></td>
<td class="colLastEvent"><?php echo $LastEvent ? $LastEvent->link_to($LastEvent->Id().' at '.$LastEvent->StartDateTime()) : 'none'?></td>
<td class="colMinGap"><?php echo $MinGap ?></td>
<td class="colMaxGap"><?php echo $MaxGap ?></td>
<td class="colFileMissing<?php echo count($FileMissing) ? ' errorText' : ''?>">
<?php echo count($FileMissing) ? '<a href="?view='.ZM_WEB_EVENTS_VIEW.'&amp;page=1'.$FileMissing_filter['query'].'">'.count($FileMissing).'</a>' : '0' ?>
<?php echo count($FileMissing) ? '<a href="?view='.ZM_WEB_EVENTS_VIEW.'&amp;page=1'.$FileMissing_filter->querystring().'">'.count($FileMissing).'</a>' : '0' ?>
</td>
<td class="colZeroSize<?php echo count($ZeroSize) ? ' errorText' : ''?>">
<?php echo count($ZeroSize) ? '<a href="?view='.ZM_WEB_EVENTS_VIEW.'&amp;page=1'.$ZeroSize_filter['query'].'">'.count($ZeroSize).'</a>' : '0' ?>
<?php echo count($ZeroSize) ? '<a href="?view='.ZM_WEB_EVENTS_VIEW.'&amp;page=1'.$ZeroSize_filter->querystring().'">'.count($ZeroSize).'</a>' : '0' ?>
</td>
</tr>
<?php

View File

@ -44,7 +44,9 @@ xhtmlHeaders(__FILE__, translate('Stats')." - ".$eid." - ".$fid );
<div id="content" class="row justify-content-center">
<form name="contentForm" id="contentForm" method="get" action="?">
<input type="hidden" name="view" value="none"/>
<div class="table-responsive-sm">
<?php echo getStatsTableHTML($eid, $fid) ?>
</div>
</form>
</div>
</div>

View File

@ -145,7 +145,7 @@ $tree = false;
if ( isset($_REQUEST['filter']) ) {
$filter = ZM\Filter::parse($_REQUEST['filter']);
$tree = $filter->tree();
ZM\Warning("Parse tree: " . print_r($tree,true));
ZM\Debug('Parse tree: ' . print_r($tree,true));
}
if ( isset($_REQUEST['range']) )

View File

@ -172,17 +172,14 @@ if ( canView('Events') && ($monitor->Type() != 'WebSite') ) {
data-show-columns="true"
data-show-export="true"
data-uncheckAll="true"
data-mobile-responsive="true"
data-buttons-class="btn btn-normal"
data-auto-refresh="true"
data-auto-refresh-silent="true"
data-show-refresh="true"
data-auto-refresh-interval="5"
class="table-sm table-borderless"
>
<thead>
<!-- Row styling is handled by bootstrap-tables -->
<tr>
<th data-sortable="false" data-field="Delete"><?php echo translate('Delete') ?></th>
<th data-sortable="false" data-field="Id"><?php echo translate('Id') ?></th>
<th data-sortable="false" data-field="Name"><?php echo translate('Name') ?></th>
<th data-sortable="false" data-field="StartDateTime"><?php echo translate('AttrStartTime') ?></th>
@ -191,7 +188,7 @@ if ( canView('Events') && ($monitor->Type() != 'WebSite') ) {
<th data-sortable="false" data-field="AlarmFrames"><?php echo translate('AlarmBrFrames') ?></th>
<th data-sortable="false" data-field="AvgScore"><?php echo translate('AvgBrScore') ?></th>
<th data-sortable="false" data-field="MaxScore"><?php echo translate('MaxBrScore') ?></th>
<th data-sortable="false" data-field="Delete"><?php echo translate('Delete') ?></th>
<th data-sortable="false" data-field="Thumbnail"><?php echo translate('Thumbnail') ?></th>
</tr>
</thead>

View File

@ -56,6 +56,7 @@ if ( $errorText ) {
}
if ( ! ($fh = @fopen($path, 'rb') ) ) {
ZM\Error('Can\'t open video at '.$path);
header('HTTP/1.0 404 Not Found');
die();
}