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

This commit is contained in:
Isaac Connor 2020-10-31 10:47:25 -04:00
commit a04f6df06a
28 changed files with 299 additions and 485 deletions

View File

@ -303,6 +303,7 @@ CREATE TABLE `Filters` (
`UpdateDiskSpace` tinyint(3) unsigned NOT NULL default '0', `UpdateDiskSpace` tinyint(3) unsigned NOT NULL default '0',
`Background` tinyint(1) unsigned NOT NULL default '0', `Background` tinyint(1) unsigned NOT NULL default '0',
`Concurrent` tinyint(1) unsigned NOT NULL default '0', `Concurrent` tinyint(1) unsigned NOT NULL default '0',
`LockRows` tinyint(1) unsigned NOT NULL default '0',
PRIMARY KEY (`Id`), PRIMARY KEY (`Id`),
KEY `Name` (`Name`) KEY `Name` (`Name`)
) ENGINE=@ZM_MYSQL_ENGINE@; ) ENGINE=@ZM_MYSQL_ENGINE@;
@ -315,6 +316,7 @@ DROP TABLE IF EXISTS `Frames`;
CREATE TABLE `Frames` ( CREATE TABLE `Frames` (
`Id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `Id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`EventId` BIGINT UNSIGNED NOT NULL default '0', `EventId` BIGINT UNSIGNED NOT NULL default '0',
FOREIGN KEY (`EventId`) REFERENCES `Events` (`Id`) ON DELETE CASCADE,
`FrameId` int(10) unsigned NOT NULL default '0', `FrameId` int(10) unsigned NOT NULL default '0',
`Type` enum('Normal','Bulk','Alarm') NOT NULL default 'Normal', `Type` enum('Normal','Bulk','Alarm') NOT NULL default 'Normal',
`TimeStamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, `TimeStamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
@ -608,8 +610,11 @@ DROP TABLE IF EXISTS `Stats`;
CREATE TABLE `Stats` ( CREATE TABLE `Stats` (
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT, `Id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`MonitorId` int(10) unsigned NOT NULL default '0', `MonitorId` int(10) unsigned NOT NULL default '0',
FOREIGN KEY (`MonitorId`) REFERENCES `Monitors` (`Id`) ON DELETE CASCADE,
`ZoneId` int(10) unsigned NOT NULL default '0', `ZoneId` int(10) unsigned NOT NULL default '0',
FOREIGN KEY (`ZoneId`) REFERENCES `Zones` (`Id`) ON DELETE CASCADE,
`EventId` BIGINT UNSIGNED NOT NULL, `EventId` BIGINT UNSIGNED NOT NULL,
FOREIGN KEY (`EventId`) REFERENCES `Events` (`Id`) ON DELETE CASCADE,
`FrameId` int(10) unsigned NOT NULL default '0', `FrameId` int(10) unsigned NOT NULL default '0',
`PixelDiff` tinyint(3) unsigned NOT NULL default '0', `PixelDiff` tinyint(3) unsigned NOT NULL default '0',
`AlarmPixels` int(10) unsigned NOT NULL default '0', `AlarmPixels` int(10) unsigned NOT NULL default '0',
@ -704,6 +709,7 @@ DROP TABLE IF EXISTS `Zones`;
CREATE TABLE `Zones` ( CREATE TABLE `Zones` (
`Id` int(10) unsigned NOT NULL auto_increment, `Id` int(10) unsigned NOT NULL auto_increment,
`MonitorId` int(10) unsigned NOT NULL default '0', `MonitorId` int(10) unsigned NOT NULL default '0',
FOREIGN KEY (`MonitorId`) REFERENCES `Monitors` (`Id`) ON DELETE CASCADE,
`Name` varchar(64) NOT NULL default '', `Name` varchar(64) NOT NULL default '',
`Type` enum('Active','Inclusive','Exclusive','Preclusive','Inactive','Privacy') NOT NULL default 'Active', `Type` enum('Active','Inclusive','Exclusive','Preclusive','Inactive','Privacy') NOT NULL default 'Active',
`Units` enum('Pixels','Percent') NOT NULL default 'Pixels', `Units` enum('Pixels','Percent') NOT NULL default 'Pixels',

83
db/zm_update-1.35.11.sql Normal file
View File

@ -0,0 +1,83 @@
/* Change Id type to BIGINT. */
set @exist := (SELECT count(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = 'Events' AND COLUMN_NAME = 'Id' and DATA_TYPE='bigint');
set @sqlstmt := if( @exist = 0, "ALTER TABLE Events MODIFY Id bigint unsigned NOT NULL auto_increment", "SELECT 'Ok'");
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='Frames' and column_name='EventId' and referenced_table_name='Events' 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 EventId in Frames already exists'", @sqlstmt);
set @sqlstmt := if( @exist = 0, "SELECT 'Adding foreign key for EventId to Frames'", @sqlstmt);
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @sqlstmt := if( @exist != 0, "SELECT '.'", "SELECT 'Deleting unlinked Frames'");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @sqlstmt := if( @exist != 0, "SELECT '.'", "DELETE FROM Frames WHERE EventId NOT IN (SELECT Id FROM Events)");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @sqlstmt := if( @exist != 0, "SELECT 'Ok'", "ALTER TABLE Frames ADD FOREIGN KEY (EventId) REFERENCES Events (Id) ON DELETE CASCADE");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
SELECT 'Adding foreign key for EventId to Stats';
set @exist := (select count(*) FROM information_schema.key_column_usage where table_name='Stats' and column_name='EventId' and referenced_table_name='Events' 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 already EventId in Stats already exists'", @sqlstmt);
set @sqlstmt := if( @exist = 0, "SELECT 'Adding FOREIGN KEY for EventId to Stats'", @sqlstmt);
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @sqlstmt := if( @exist > 0, "SELECT 'FOREIGN KEY for EventId in Stats already exists'", "DELETE FROM Stats WHERE EventId NOT IN (SELECT Id FROM Events);");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @sqlstmt := if( @exist > 0, "SELECT 'Ok'", "ALTER TABLE Stats ADD FOREIGN KEY (EventId) REFERENCES Events (Id) ON DELETE CASCADE");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
SELECT 'Adding foreign key for MonitorId to Stats';
set @exist := (select count(*) FROM information_schema.key_column_usage where table_name='Stats' 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'");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @sqlstmt := if( @exist > 0, "SELECT 'FOREIGN KEY for MonitorId in Stats already exists'", "DELETE FROM Stats WHERE MonitorId NOT IN (SELECT Id FROM Monitors);");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @sqlstmt := if( @exist > 0, "SELECT 'Ok'", "ALTER TABLE Stats ADD FOREIGN KEY (MonitorId) REFERENCES Monitors (Id) ON DELETE CASCADE");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
SELECT 'Adding foreign key for ZoneId to Stats';
set @exist := (select count(*) FROM information_schema.key_column_usage where table_name='Stats' and column_name='ZoneId' and referenced_table_name='Zones' and referenced_column_name='Id');
set @sqlstmt := if( @exist > 1, "SELECT 'You have more than 1 FOREIGN KEY. Please do manual cleanup'", "SELECT 'Ok'");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @sqlstmt := if( @exist > 0, "SELECT 'FOREIGN KEY for ZoneId in Stats already exists'", "DELETE FROM Stats WHERE ZoneId NOT IN (SELECT Id FROM Zones);");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @sqlstmt := if( @exist > 0, "SELECT 'Ok'", "ALTER TABLE Stats ADD FOREIGN KEY (ZoneId) REFERENCES Zones (Id) ON DELETE CASCADE");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
SELECT 'Adding foreign key for MonitorId to Zones';
set @exist := (select count(*) FROM information_schema.key_column_usage where table_name='Zones' 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'");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;
set @sqlstmt := if( @exist > 0, "SELECT 'FOREIGN KEY for MonitorId in Zones already exists'", "SELECT 'FOREIGN KEY for MonitorId in Zones does not already exist'");
set @badzones := (select count(*) FROM Zones WHERE MonitorId NOT IN (SELECT Id FROM Monitors));
set @sqlstmt := if ( @badzones > 0, "SELECT 'You have Zones with no Monitor record in the Monitors table. Please delete them manually'", "ALTER TABLE Zones ADD FOREIGN KEY (MonitorId) REFERENCES Monitors (Id)");
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;

18
db/zm_update-1.35.12.sql Normal file
View File

@ -0,0 +1,18 @@
--
-- Update Filters table to have a LockRows Column
--
SELECT 'Checking for LockRows in Filters';
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Filters'
AND table_schema = DATABASE()
AND column_name = 'LockRows'
) > 0,
"SELECT 'Column LockRows already exists in Filters'",
"ALTER TABLE Filters ADD COLUMN `LockRows` tinyint(1) unsigned NOT NULL default '0' AFTER `Concurrent`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

View File

@ -39,9 +39,9 @@ if [ "$1" = "configure" ]; then
exit 1; exit 1;
fi fi
# This creates the user. # This creates the user.
echo "grant lock tables, alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql echo "grant lock tables, alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute, REFERENCES on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
else else
echo "grant lock tables, alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql echo "grant lock tables, alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute, REFERENCES on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
fi fi
zmupdate.pl --nointeractive zmupdate.pl --nointeractive

View File

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

View File

@ -67,8 +67,8 @@ if [ "$1" = "configure" ]; then
# This creates the user. # This creates the user.
echo "CREATE USER '${ZM_DB_USER}'@localhost IDENTIFIED BY '${ZM_DB_PASS}';" | mysql --defaults-file=/etc/mysql/debian.cnf mysql echo "CREATE USER '${ZM_DB_USER}'@localhost IDENTIFIED BY '${ZM_DB_PASS}';" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
fi fi
echo "Updating permissions" echo "Updating permissions for user ${ZM_DB_USER}@localhost"
echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql echo "GRANT LOCK TABLES,ALTER,DROP,SELECT,INSERT,UPDATE,DELETE,CREATE,INDEX,ALTER ROUTINE,CREATE ROUTINE, TRIGGER,EXECUTE,REFERENCES ON ${ZM_DB_NAME}.* TO '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
zmupdate.pl --nointeractive zmupdate.pl --nointeractive
zmupdate.pl --nointeractive -f zmupdate.pl --nointeractive -f

View File

@ -25,8 +25,8 @@ create_update_user () {
# This creates the user. # This creates the user.
echo "CREATE USER '${ZM_DB_USER}'@${ZM_DB_HOST} IDENTIFIED BY '${ZM_DB_PASS}';" | mysql --defaults-file=/etc/mysql/debian.cnf mysql echo "CREATE USER '${ZM_DB_USER}'@${ZM_DB_HOST} IDENTIFIED BY '${ZM_DB_PASS}';" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
fi fi
echo "Updating permissions" echo "Updating permissions for user ${ZM_DB_USER}@${ZM_DB_HOST}"
echo "GRANT LOCK tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine,trigger,execute ON ${ZM_DB_NAME}.* TO '${ZM_DB_USER}'@${ZM_DB_HOST};" | mysql --defaults-file=/etc/mysql/debian.cnf mysql echo "GRANT LOCK tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine,trigger,execute,REFERENCES ON ${ZM_DB_NAME}.* TO '${ZM_DB_USER}'@${ZM_DB_HOST};" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
} }
update_db () { update_db () {

View File

@ -349,6 +349,10 @@ sub GenerateVideo {
return; return;
} # end sub GenerateVideo } # end sub GenerateVideo
# Note about transactions, this function may be called with rows locked and hence in a transaction.
# So we will detect if we are in a transaction, and if not, start one. We will NOT do rollback or
# commits unless we started the transaction.
sub delete { sub delete {
my $event = $_[0]; my $event = $_[0];
@ -378,23 +382,25 @@ sub delete {
Info("Deleting event $event->{Id} from Monitor $event->{MonitorId} StartTime:$event->{StartTime} from ".$event->Path()); Info("Deleting event $event->{Id} from Monitor $event->{MonitorId} StartTime:$event->{StartTime} from ".$event->Path());
$ZoneMinder::Database::dbh->ping(); $ZoneMinder::Database::dbh->ping();
$ZoneMinder::Database::dbh->begin_work(); my $in_transaction = $ZoneMinder::Database::dbh->{AutoCommit} ? 0 : 1;
#$event->lock_and_load();
ZoneMinder::Database::zmDbDo('DELETE FROM Frames WHERE EventId=?', $$event{Id}); $ZoneMinder::Database::dbh->begin_work() if ! $in_transaction;
if ( $ZoneMinder::Database::dbh->errstr() ) {
$ZoneMinder::Database::dbh->commit(); # Going to delete in order of least value to greatest value. Stats is least and references Frames
return;
}
ZoneMinder::Database::zmDbDo('DELETE FROM Stats WHERE EventId=?', $$event{Id}); ZoneMinder::Database::zmDbDo('DELETE FROM Stats WHERE EventId=?', $$event{Id});
if ( $ZoneMinder::Database::dbh->errstr() ) { if ( $ZoneMinder::Database::dbh->errstr() ) {
$ZoneMinder::Database::dbh->commit(); $ZoneMinder::Database::dbh->commit() if ! $in_transaction;
return;
}
ZoneMinder::Database::zmDbDo('DELETE FROM Frames WHERE EventId=?', $$event{Id});
if ( $ZoneMinder::Database::dbh->errstr() ) {
$ZoneMinder::Database::dbh->commit() if ! $in_transaction;
return; return;
} }
# Do it individually to avoid locking up the table for new events # Do it individually to avoid locking up the table for new events
ZoneMinder::Database::zmDbDo('DELETE FROM Events WHERE Id=?', $$event{Id}); ZoneMinder::Database::zmDbDo('DELETE FROM Events WHERE Id=?', $$event{Id});
$ZoneMinder::Database::dbh->commit(); $ZoneMinder::Database::dbh->commit() if ! $in_transaction;
} }
if ( ( $in_zmaudit or (!$Config{ZM_OPT_FAST_DELETE})) and $event->Storage()->DoDelete() ) { if ( ( $in_zmaudit or (!$Config{ZM_OPT_FAST_DELETE})) and $event->Storage()->DoDelete() ) {

View File

@ -77,6 +77,7 @@ UpdateDiskSpace
UserId UserId
Background Background
Concurrent Concurrent
LockRows
); );
sub Execute { sub Execute {
@ -103,6 +104,8 @@ sub Execute {
$sql =~ s/zmSystemLoad/$load/g; $sql =~ s/zmSystemLoad/$load/g;
} }
$sql .= ' FOR UPDATE' if $$self{LockRows};
Debug("Filter::Execute SQL ($sql)"); Debug("Filter::Execute SQL ($sql)");
my $sth = $ZoneMinder::Database::dbh->prepare_cached($sql) my $sth = $ZoneMinder::Database::dbh->prepare_cached($sql)
or Fatal("Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr()); or Fatal("Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr());

View File

@ -504,9 +504,9 @@ sub openFile {
$LOGFILE->autoflush() if $this->{autoFlush}; $LOGFILE->autoflush() if $this->{autoFlush};
my $webUid = (getpwnam($ZoneMinder::Config::Config{ZM_WEB_USER}))[2]; my $webUid = (getpwnam($ZoneMinder::Config::Config{ZM_WEB_USER}))[2];
Error("Can't get uid for $ZoneMinder::Config::Config{ZM_WEB_USER}") if ! defined $webUid; Error('Can\'t get uid for '.$ZoneMinder::Config::Config{ZM_WEB_USER}) if ! defined $webUid;
my $webGid = (getgrnam($ZoneMinder::Config::Config{ZM_WEB_GROUP}))[2]; my $webGid = (getgrnam($ZoneMinder::Config::Config{ZM_WEB_GROUP}))[2];
Error("Can't get gid for $ZoneMinder::Config::Config{ZM_WEB_USER}") if ! defined $webGid; Error('Can\'t get gid for '.$ZoneMinder::Config::Config{ZM_WEB_USER}) if ! defined $webGid;
if ( $> == 0 ) { if ( $> == 0 ) {
# If we are root, we want to make sure that www-data or whatever owns the file # If we are root, we want to make sure that www-data or whatever owns the file
chown($webUid, $webGid, $this->{logFile} ) or chown($webUid, $webGid, $this->{logFile} ) or
@ -610,6 +610,7 @@ sub logInit( ;@ ) {
my %options = @_ ? @_ : (); my %options = @_ ? @_ : ();
$logger = ZoneMinder::Logger->new() if !$logger; $logger = ZoneMinder::Logger->new() if !$logger;
$logger->initialise(%options); $logger->initialise(%options);
logSetSignal();
} }
sub logReinit { sub logReinit {
@ -626,12 +627,26 @@ sub logHupHandler {
$do_log_rotate = 1; $do_log_rotate = 1;
} }
sub logUSR1Handler {
$logger->level($logger->level()+1);
Info('Logger - Level changed to '. $logger->level() . '=>'.$codes{$logger->level()});
}
sub logUSR2Handler {
$logger->level($logger->level()-1);
Info('Logger - Level changed to '. $logger->level() . '=>'.$codes{$logger->level()});
}
sub logSetSignal { sub logSetSignal {
$SIG{HUP} = \&logHupHandler; $SIG{HUP} = \&logHupHandler;
$SIG{USR1} = \&logUSR1Handler;
$SIG{USR2} = \&logUSR2Handler;
} }
sub logClearSignal { sub logClearSignal {
$SIG{HUP} = 'DEFAULT'; $SIG{HUP} = 'DEFAULT';
$SIG{USR1} = 'DEFAULT';
$SIG{USR2} = 'DEFAULT';
} }
sub logLevel { sub logLevel {

View File

@ -277,6 +277,7 @@ FILTER: while( my $db_filter = $sth->fetchrow_hashref() ) {
sub checkFilter { sub checkFilter {
my $filter = shift; my $filter = shift;
my $in_transaction = ZoneMinder::Database::start_transaction($dbh) if $$filter{LockRows};
my @Events = $filter->Execute(); my @Events = $filter->Execute();
Debug( Debug(
join(' ', join(' ',
@ -396,6 +397,7 @@ sub checkFilter {
$ZoneMinder::Database::dbh->commit(); $ZoneMinder::Database::dbh->commit();
} # end if UpdateDiskSpace } # end if UpdateDiskSpace
} # end foreach event } # end foreach event
ZoneMinder::Database::end_transaction($dbh, $in_transaction) if $$filter{LockRows};
} # end sub checkFilter } # end sub checkFilter
sub generateVideo { sub generateVideo {

View File

@ -664,7 +664,6 @@ int FfmpegCamera::OpenFfmpeg() {
} // int FfmpegCamera::OpenFfmpeg() } // int FfmpegCamera::OpenFfmpeg()
int FfmpegCamera::Close() { int FfmpegCamera::Close() {
Debug(2, "CloseFfmpeg called.");
mCanCapture = false; mCanCapture = false;

View File

@ -29,15 +29,13 @@
bool zm_reload = false; bool zm_reload = false;
bool zm_terminate = false; bool zm_terminate = false;
RETSIGTYPE zm_hup_handler(int signal) RETSIGTYPE zm_hup_handler(int signal) {
{
// Shouldn't do complex things in signal handlers, logging is complex and can block due to mutexes. // Shouldn't do complex things in signal handlers, logging is complex and can block due to mutexes.
//Info("Got signal %d (%s), reloading", signal, strsignal(signal)); //Info("Got signal %d (%s), reloading", signal, strsignal(signal));
zm_reload = true; zm_reload = true;
} }
RETSIGTYPE zm_term_handler(int signal) RETSIGTYPE zm_term_handler(int signal) {
{
// Shouldn't do complex things in signal handlers, logging is complex and can block due to mutexes. // Shouldn't do complex things in signal handlers, logging is complex and can block due to mutexes.
//Info("Got signal %d (%s), exiting", signal, strsignal(signal)); //Info("Got signal %d (%s), exiting", signal, strsignal(signal));
zm_terminate = true; zm_terminate = true;
@ -56,7 +54,6 @@ RETSIGTYPE zm_die_handler(int signal)
void *ip = nullptr; void *ip = nullptr;
void *cr2 = nullptr; void *cr2 = nullptr;
if ( info && context ) { if ( info && context ) {
Debug(1, Debug(1,
"Signal information: number %d code %d errno %d pid %d uid %d status %d", "Signal information: number %d code %d errno %d pid %d uid %d status %d",
signal, info->si_code, info->si_errno, info->si_pid, signal, info->si_code, info->si_errno, info->si_pid,
@ -115,8 +112,7 @@ RETSIGTYPE zm_die_handler(int signal)
exit(signal); exit(signal);
} }
void zmSetHupHandler(SigHandler * handler) void zmSetHupHandler(SigHandler * handler) {
{
sigset_t block_set; sigset_t block_set;
sigemptyset(&block_set); sigemptyset(&block_set);
struct sigaction action, old_action; struct sigaction action, old_action;
@ -127,8 +123,7 @@ void zmSetHupHandler(SigHandler * handler)
sigaction(SIGHUP, &action, &old_action); sigaction(SIGHUP, &action, &old_action);
} }
void zmSetTermHandler(SigHandler * handler) void zmSetTermHandler(SigHandler * handler) {
{
sigset_t block_set; sigset_t block_set;
sigemptyset(&block_set); sigemptyset(&block_set);
struct sigaction action, old_action; struct sigaction action, old_action;
@ -141,8 +136,7 @@ void zmSetTermHandler(SigHandler * handler)
sigaction(SIGQUIT, &action, &old_action); sigaction(SIGQUIT, &action, &old_action);
} }
void zmSetDieHandler(SigHandler * handler) void zmSetDieHandler(SigHandler * handler) {
{
sigset_t block_set; sigset_t block_set;
sigemptyset(&block_set); sigemptyset(&block_set);
struct sigaction action, old_action; struct sigaction action, old_action;
@ -163,18 +157,15 @@ void zmSetDieHandler(SigHandler * handler)
sigaction(SIGFPE, &action, &old_action); sigaction(SIGFPE, &action, &old_action);
} }
void zmSetDefaultHupHandler() void zmSetDefaultHupHandler() {
{
zmSetHupHandler((SigHandler *) zm_hup_handler); zmSetHupHandler((SigHandler *) zm_hup_handler);
} }
void zmSetDefaultTermHandler() void zmSetDefaultTermHandler() {
{
zmSetTermHandler((SigHandler *) zm_term_handler); zmSetTermHandler((SigHandler *) zm_term_handler);
} }
void zmSetDefaultDieHandler() void zmSetDefaultDieHandler() {
{
if ( config.dump_cores ) { if ( config.dump_cores ) {
// Do nothing // Do nothing
} else { } else {

View File

@ -1 +1 @@
1.35.10 1.35.12

View File

@ -115,6 +115,19 @@ function deleteRequest($eid) {
} }
function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $limit) { function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $limit) {
$data = array(
'total' => 0,
'totalNotFiltered' => 0,
'rows' => array(),
'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG)
);
$failed = !$filter->test_pre_sql_conditions();
if ( $failed ) {
ZM\Debug('Pre conditions failed, not doing sql');
return $data;
}
// Put server pagination code here // Put server pagination code here
// The table we want our data from // The table we want our data from
$table = 'Events'; $table = 'Events';
@ -126,7 +139,8 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
$col_alt = array('Monitor', 'Storage'); $col_alt = array('Monitor', 'Storage');
if ( !in_array($sort, array_merge($columns, $col_alt)) ) { if ( !in_array($sort, array_merge($columns, $col_alt)) ) {
ZM\Fatal('Invalid sort field: ' . $sort); ZM\Error('Invalid sort field: ' . $sort);
$sort = 'Id';
} }
$data = array(); $data = array();
@ -140,44 +154,39 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
if ( count($advsearch) ) { if ( count($advsearch) ) {
foreach ( $advsearch as $col=>$text ) { foreach ( $advsearch as $col=>$text ) {
if ( !in_array($col, array_merge($columns, $col_alt)) ) { if ( in_array($col, $columns) ) {
array_push($likes, 'E.'.$col.' LIKE ?');
array_push($query['values'], $text);
} else if ( in_array($col, $col_alt) ) {
array_push($likes, 'M.'.$col.' LIKE ?');
array_push($query['values'], $text);
} else {
ZM\Error("'$col' is not a sortable column name"); ZM\Error("'$col' is not a sortable column name");
continue; continue;
} }
$text = '%' .$text. '%'; } # end foreach col in advsearch
array_push($likes, $col.' LIKE ?');
array_push($query['values'], $text);
}
$wherevalues = $query['values']; $wherevalues = $query['values'];
$where = ' AND (' .implode(' OR ', $likes). ')'; $where .= ($where != '') ? ' AND (' .implode(' OR ', $likes). ')' : implode(' OR ', $likes);
} else if ( $search != '' ) { } else if ( $search != '' ) {
$search = '%' .$search. '%'; $search = '%' .$search. '%';
foreach ( $columns as $col ) { foreach ( $columns as $col ) {
array_push($likes, $col.' LIKE ?'); array_push($likes, 'E.'.$col.' LIKE ?');
array_push($query['values'], $search); array_push($query['values'], $search);
} }
$wherevalues = $query['values']; $wherevalues = $query['values'];
$where = ' AND (' .implode(' OR ', $likes). ')'; $where .= ( $where != '') ? ' AND (' .implode(' OR ', $likes). ')' : implode(' OR ', $likes);
} }
if ( $where ) if ( $where )
$where = ' WHERE '.$where; $where = ' WHERE '.$where;
$sort = $sort == "Monitor" ? 'M.Name' : 'E.'.$sort; $sort = $sort == 'Monitor' ? 'M.Name' : 'E.'.$sort;
$col_str = 'E.*, M.Name AS Monitor'; $col_str = 'E.*, M.Name AS Monitor';
//$query['sql'] = 'SELECT ' .$col_str. ' FROM `' .$table. '` AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'.$where.' ORDER BY LENGTH(' .$sort. '), ' .$sort. ' ' .$order. ' LIMIT ?, ?';
$query['sql'] = 'SELECT ' .$col_str. ' FROM `' .$table. '` AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'.$where.' ORDER BY ' .$sort. ' ' .$order. ' LIMIT ?, ?'; $query['sql'] = 'SELECT ' .$col_str. ' FROM `' .$table. '` AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'.$where.' ORDER BY ' .$sort. ' ' .$order. ' LIMIT ?, ?';
array_push($query['values'], $offset, $limit); array_push($query['values'], $offset, $limit);
ZM\Warning('Calling the following sql query: ' .$query['sql']); //ZM\Debug('Calling the following sql query: ' .$query['sql']);
$data['totalNotFiltered'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table . ' AS E'. ($filter->sql() ? ' WHERE '.$filter->sql():''), 'Total');
if ( $search != '' || count($advsearch) ) {
$data['total'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table . ' AS E'.$where , 'Total', $wherevalues);
} else {
$data['total'] = $data['totalNotFiltered'];
}
$storage_areas = ZM\Storage::find(); $storage_areas = ZM\Storage::find();
$StorageById = array(); $StorageById = array();
@ -187,8 +196,11 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
$rows = array(); $rows = array();
foreach ( dbFetchAll($query['sql'], NULL, $query['values']) as $row ) { foreach ( dbFetchAll($query['sql'], NULL, $query['values']) as $row ) {
ZM\Debug("row".print_r($row,true));
$event = new ZM\Event($row); $event = new ZM\Event($row);
if ( !$filter->test_post_sql_conditions($event) ) {
$event->remove_from_cache();
continue;
}
$scale = intval(5*100*ZM_WEB_LIST_THUMB_WIDTH / $event->Width()); $scale = intval(5*100*ZM_WEB_LIST_THUMB_WIDTH / $event->Width());
$imgSrc = $event->getThumbnailSrc(array(),'&'); $imgSrc = $event->getThumbnailSrc(array(),'&');
$streamSrc = $event->getStreamSrc(array( $streamSrc = $event->getStreamSrc(array(
@ -201,16 +213,18 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
$row['Emailed'] = $row['Emailed'] ? translate('Yes') : translate('No'); $row['Emailed'] = $row['Emailed'] ? translate('Yes') : translate('No');
$row['Cause'] = validHtmlStr($row['Cause']); $row['Cause'] = validHtmlStr($row['Cause']);
$row['StartTime'] = strftime(STRF_FMT_DATETIME_SHORTER, strtotime($row['StartTime'])); $row['StartTime'] = strftime(STRF_FMT_DATETIME_SHORTER, strtotime($row['StartTime']));
$row['EndTime'] = strftime(STRF_FMT_DATETIME_SHORTER, strtotime($row['StartTime'])); $row['EndTime'] = strftime(STRF_FMT_DATETIME_SHORTER, strtotime($row['EndTime']));
$row['Length'] = gmdate('H:i:s', $row['Length'] ); $row['Length'] = gmdate('H:i:s', $row['Length'] );
$row['Storage'] = ( $row['StorageId'] and isset($StorageById[$row['StorageId']]) ) ? $StorageById[$row['StorageId']]->Name() : 'Default'; $row['Storage'] = ( $row['StorageId'] and isset($StorageById[$row['StorageId']]) ) ? $StorageById[$row['StorageId']]->Name() : 'Default';
$row['Notes'] = htmlspecialchars($row['Notes']); $row['Notes'] = nl2br(htmlspecialchars($row['Notes']));
$row['DiskSpace'] = human_filesize($event->DiskSpace()); $row['DiskSpace'] = human_filesize($event->DiskSpace());
$rows[] = $row; $rows[] = $row;
} }
$data['rows'] = $rows; $data['rows'] = $rows;
$data['updated'] = preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG);
# total has to be the # of available rows. Not sure what totalNotFiltered is actually used for yet.
$data['totalNotFiltered'] = $data['total'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table. ' AS E'. ($filter->sql() ? ' WHERE '.$filter->sql():''), 'Total');
#$data['total'] = count($rows);
return $data; return $data;
} }
?> ?>

View File

@ -66,7 +66,7 @@ if ( isset($_REQUEST['limit']) ) {
switch ( $task ) { switch ( $task ) {
case 'create' : case 'create' :
createRequest($task, $eid); createRequest();
break; break;
case 'query' : case 'query' :
$data = queryRequest($search, $advsearch, $sort, $offset, $order, $limit); $data = queryRequest($search, $advsearch, $sort, $offset, $order, $limit);

View File

@ -6,6 +6,7 @@ if ( $_REQUEST['entity'] == 'navBar' ) {
$auth_hash = generateAuthHash(ZM_AUTH_HASH_IPS); $auth_hash = generateAuthHash(ZM_AUTH_HASH_IPS);
if ( isset($_REQUEST['auth']) and ($_REQUEST['auth'] != $auth_hash) ) { if ( isset($_REQUEST['auth']) and ($_REQUEST['auth'] != $auth_hash) ) {
$data['auth'] = $auth_hash; $data['auth'] = $auth_hash;
$data['auth_relay'] = get_auth_relay();
} }
} }
// Each widget on the navbar has its own function // Each widget on the navbar has its own function

View File

@ -29,6 +29,7 @@ class Filter extends ZM_Object {
'Background' => 0, 'Background' => 0,
'Concurrent' => 0, 'Concurrent' => 0,
'Query_json' => '', 'Query_json' => '',
'LockRows' => 0,
); );
protected $_querystring; protected $_querystring;

View File

@ -408,10 +408,10 @@ class Logger {
} }
} }
$message = $code.' ['.$string.']';
if ( $level <= $this->syslogLevel ) if ( $level <= $this->syslogLevel )
syslog(self::$syslogPriorities[$level], $message); syslog(self::$syslogPriorities[$level], $message);
$message = $code.' ['.$string.']';
if ( $level <= $this->databaseLevel ) { if ( $level <= $this->databaseLevel ) {
try { try {
global $dbConn; global $dbConn;

View File

@ -371,6 +371,7 @@ $SLANG = array(
'FilterUpdateDiskSpace' => 'Update used disk space', 'FilterUpdateDiskSpace' => 'Update used disk space',
'FilterDeleteEvents' => 'Delete all matches', 'FilterDeleteEvents' => 'Delete all matches',
'FilterCopyEvents' => 'Copy all matches', 'FilterCopyEvents' => 'Copy all matches',
'FilterLockRows' => 'Lock Rows',
'FilterMoveEvents' => 'Move all matches', 'FilterMoveEvents' => 'Move all matches',
'FilterEmailEvents' => 'Email details of all matches', 'FilterEmailEvents' => 'Email details of all matches',
'FilterEmailTo' => 'Email To', 'FilterEmailTo' => 'Email To',

View File

@ -351,6 +351,11 @@ if ( currentView != 'none' && currentView != 'login' ) {
.done(setNavBar) .done(setNavBar)
.fail(function(jqxhr, textStatus, error) { .fail(function(jqxhr, textStatus, error) {
console.log("Request Failed: " + textStatus + ", " + error); console.log("Request Failed: " + textStatus + ", " + error);
if ( ! jqxhr.responseText ) {
console.log("No responseText in jqxhr");
console.log(jqxhr);
return;
}
console.log("Response Text: " + jqxhr.responseText.replace(/(<([^>]+)>)/gi, '')); console.log("Response Text: " + jqxhr.responseText.replace(/(<([^>]+)>)/gi, ''));
if ( textStatus != "timeout" ) { if ( textStatus != "timeout" ) {
// The idea is that this should only fail due to auth, so reload the page // The idea is that this should only fail due to auth, so reload the page
@ -367,10 +372,14 @@ if ( currentView != 'none' && currentView != 'login' ) {
} }
if ( data.auth ) { if ( data.auth ) {
if ( data.auth != auth_hash ) { if ( data.auth != auth_hash ) {
console.log("Update auth_hash to "+data.auth);
// Update authentication token. // Update authentication token.
auth_hash = data.auth; auth_hash = data.auth;
} }
} }
if ( data.auth_relay ) {
auth_relay = data.auth_relay;
}
// iterate through all the keys then update each element id with the same name // iterate through all the keys then update each element id with the same name
for (var key of Object.keys(data)) { for (var key of Object.keys(data)) {
if ( key == "auth" ) continue; if ( key == "auth" ) continue;

View File

@ -42,73 +42,6 @@ if ( isset($_REQUEST['filter'])) {
parseSort(); parseSort();
$filterQuery = $filter->querystring(); $filterQuery = $filter->querystring();
ZM\Debug('Filter '.print_r($filter, true));
if ( $filter->sql() ) {
$eventsSql .= ' AND ('.$filter->sql().')';
} else {
ZM\Warning('No filters');
exit;
}
$eventsSql .= ' ORDER BY '.$sortColumn.' '.$sortOrder;
if ( $sortColumn != 'E.Id' ) $eventsSql .= ',E.Id '.$sortOrder;
$page = isset($_REQUEST['page']) ? validInt($_REQUEST['page']) : 0;
$limit = isset($_REQUEST['limit']) ? validInt($_REQUEST['limit']) : $filter['limit'];
if ( $_POST ) {
// I think this is basically so that a refresh doesn't repost
ZM\Debug('Redirecting to ' . $_SERVER['REQUEST_URI']);
header('Location: ?view=' . $view.htmlspecialchars_decode($filterQuery).htmlspecialchars_decode($sortQuery).$limitQuery.'&page='.$page);
exit();
}
$failed = !$filter->test_pre_sql_conditions();
if ( $failed ) {
ZM\Debug('Pre conditions failed, not doing sql');
}
$results = $failed ? null : dbQuery($eventsSql);
$nEvents = $results ? $results->rowCount() : 0;
if ( ! $results ) {
global $error_message;
$error_message = dbError($eventsSql);
}
ZM\Debug("Pre conditions succeeded sql return $nEvents events");
if ( !empty($limit) && ($nEvents > $limit) ) {
$nEvents = $limit;
}
$pages = (int)ceil($nEvents/ZM_WEB_EVENTS_PER_PAGE);
#Debug("Page $page Limit $limit #vents: $nEvents pages: $pages ");
if ( !empty($page) ) {
if ( $page < 0 )
$page = 1;
else if ( $pages and ( $page > $pages ) )
$page = $pages;
$limitStart = (($page-1)*ZM_WEB_EVENTS_PER_PAGE);
if ( empty($limit) ) {
$limitAmount = ZM_WEB_EVENTS_PER_PAGE;
} else {
$limitLeft = $limit - $limitStart;
$limitAmount = ($limitLeft>ZM_WEB_EVENTS_PER_PAGE)?ZM_WEB_EVENTS_PER_PAGE:$limitLeft;
}
$eventsSql .= ' LIMIT '.$limitStart.', '.$limitAmount;
} else if ( !empty($limit) ) {
$eventsSql .= ' LIMIT 0, '.$limit;
}
$maxShortcuts = 5;
$focusWindow = true;
$storage_areas = ZM\Storage::find();
$StorageById = array();
foreach ( $storage_areas as $S ) {
$StorageById[$S->Id()] = $S;
}
xhtmlHeaders(__FILE__, translate('Events')); xhtmlHeaders(__FILE__, translate('Events'));
getBodyTopHTML(); getBodyTopHTML();
@ -136,6 +69,8 @@ getBodyTopHTML();
<table <table
id="eventTable" id="eventTable"
data-locale="<?php echo i18n() ?>" data-locale="<?php echo i18n() ?>"
data-side-pagination="server"
data-ajax="ajaxRequest"
data-pagination="true" data-pagination="true"
data-show-pagination-switch="true" data-show-pagination-switch="true"
data-page-list="[10, 25, 50, 100, 200, All]" data-page-list="[10, 25, 50, 100, 200, All]"
@ -155,6 +90,7 @@ getBodyTopHTML();
data-mobile-responsive="true" data-mobile-responsive="true"
data-buttons-class="btn btn-normal" data-buttons-class="btn btn-normal"
data-show-jump-to="true" data-show-jump-to="true"
data-show-refresh="true"
class="table-sm table-borderless" class="table-sm table-borderless"
style="display:none;" style="display:none;"
> >
@ -168,163 +104,24 @@ getBodyTopHTML();
<th data-sortable="true" data-field="Emailed"><?php echo translate('Emailed') ?></th> <th data-sortable="true" data-field="Emailed"><?php echo translate('Emailed') ?></th>
<th data-sortable="true" data-field="Monitor"><?php echo translate('Monitor') ?></th> <th data-sortable="true" data-field="Monitor"><?php echo translate('Monitor') ?></th>
<th data-sortable="true" data-field="Cause"><?php echo translate('Cause') ?></th> <th data-sortable="true" data-field="Cause"><?php echo translate('Cause') ?></th>
<th data-sortable="true" data-field="AttrStartTime"><?php echo translate('AttrStartTime') ?></th> <th data-sortable="true" data-field="StartTime"><?php echo translate('AttrStartTime') ?></th>
<th data-sortable="true" data-field="AttrEndTime"><?php echo translate('AttrEndTime') ?></th> <th data-sortable="true" data-field="EndTime"><?php echo translate('AttrEndTime') ?></th>
<th data-sortable="true" data-field="Duration"><?php echo translate('Duration') ?></th> <th data-sortable="true" data-field="Length"><?php echo translate('Duration') ?></th>
<th data-sortable="true" data-field="Frames"><?php echo translate('Frames') ?></th> <th data-sortable="true" data-field="Frames"><?php echo translate('Frames') ?></th>
<th data-sortable="true" data-field="AlarmBrFrames"><?php echo translate('AlarmBrFrames') ?></th> <th data-sortable="true" data-field="AlarmFrames"><?php echo translate('AlarmBrFrames') ?></th>
<th data-sortable="true" data-field="TotalBrScore"><?php echo translate('TotalBrScore') ?></th> <th data-sortable="true" data-field="TotScore"><?php echo translate('TotalBrScore') ?></th>
<th data-sortable="true" data-field="AvgBrScore"><?php echo translate('AvgBrScore') ?></th> <th data-sortable="true" data-field="AvgScore"><?php echo translate('AvgBrScore') ?></th>
<th data-sortable="true" data-field="MaxBrScore"><?php echo translate('MaxBrScore') ?></th> <th data-sortable="true" data-field="MaxScore"><?php echo translate('MaxBrScore') ?></th>
<?php <th data-sortable="false" data-field="Storage"><?php echo translate('Storage') ?></th>
if ( count($storage_areas) > 1 ) {
?>
<th data-sortable="true" data-field="Storage"><?php echo translate('Storage') ?></th>
<?php
}
if ( ZM_WEB_EVENT_DISK_SPACE ) {
?>
<th data-sortable="true" data-field="DiskSpace"><?php echo translate('DiskSpace') ?></th> <th data-sortable="true" data-field="DiskSpace"><?php echo translate('DiskSpace') ?></th>
<?php
}
if ( ZM_WEB_LIST_THUMBS ) {
?>
<th data-sortable="false" data-field="Thumbnail"><?php echo translate('Thumbnail') ?></th> <th data-sortable="false" data-field="Thumbnail"><?php echo translate('Thumbnail') ?></th>
<?php
}
?>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php <!-- Row data populated via Ajax -->
$count = 0;
$disk_space_total = 0;
if ( $results ) {
$events = array();
while ( $event_row = dbFetchNext($results) ) {
$event = new ZM\Event($event_row);
if ( !$filter->test_post_sql_conditions($event) ) {
$event->remove_from_cache();
continue;
}
$events[] = $event;
if ( $limit and (count($events) >= $limit) ) {
break;
}
ZM\Debug("Have " . count($events) . " events, limit $limit");
}
foreach ( $events as $event ) {
$scale = max(reScale(SCALE_BASE, $event->DefaultScale(), ZM_WEB_DEFAULT_SCALE), SCALE_BASE);
?>
<tr<?php echo $event->Archived() ? ' class="archived"' : '' ?>>
<td data-checkbox="true"></td>
<td><a href="?view=event&amp;eid=<?php echo $event->Id().$filterQuery.$sortQuery.'&amp;page=1">'.$event->Id() ?></a></td>
<td><a href="?view=event&amp;eid=<?php echo $event->Id().$filterQuery.$sortQuery.'&amp;page=1">'.validHtmlStr($event->Name())?></a>
<?php
$archived = $event->Archived() ? translate('Archived') : '';
$emailed = $event->Emailed() ? ' '.translate('Emailed') : '';
echo '<br/><div class="small text-nowrap text-muted">'.$archived.$emailed.'</div>';
?>
</td>
<td class="text-center"><?php echo ( $event->Archived() ) ? 'Yes' : 'No' ?></td>
<td class="text-center"><?php echo ( $event->Emailed() ) ? 'Yes' : 'No' ?></td>
<td><?php echo makeLink( '?view=monitor&amp;mid='.$event->MonitorId(), $event->MonitorName(), canEdit( 'Monitors' ) ) ?></td>
<td><?php echo makeLink( '#', validHtmlStr($event->Cause()), canEdit( 'Events' ), 'title="' .htmlspecialchars($event->Notes()). '" class="eDetailLink" data-eid=' .$event->Id(). '"') ?>
<?php
# display notes as small text
if ( $event->Notes() ) {
# if notes include detection objects, then link it to objdetect.jpg
if ( strpos($event->Notes(), 'detected:') !== false ) {
# make a link
echo makeLink( '?view=image&amp;eid='.$event->Id().'&amp;fid=objdetect', '<div class="small text-nowrap text-muted"><u>'.$event->Notes().'</u></div>');
} else if ( $event->Notes() != 'Forced Web: ' ) {
echo '<br/><div class="small text-nowrap text-muted">'.$event->Notes().'</div>';
}
}
?>
</td>
<td><?php echo strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->StartTime())) ?></td>
<td><?php echo strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->EndTime())) ?></td>
<td><?php echo gmdate('H:i:s', $event->Length() ) ?></td>
<td><a href="?view=frames&amp;eid=<?php echo $event->Id() ?>"><?php echo $event->Frames() ?></a></td>
<td><a href="?view=frames&amp;eid=<?php echo $event->Id() ?>"><?php echo $event->AlarmFrames() ?></a></td>
<td><?php echo $event->TotScore() ?></td>
<td><?php echo $event->AvgScore() ?></td>
<td><?php echo makeLink('?view=frame&amp;eid='.$event->Id().'&amp;fid=0', $event->MaxScore()); ?></td>
<?php
if ( count($storage_areas) > 1 ) {
?>
<td>
<?php
if ( $event->StorageId() ) {
echo isset($StorageById[$event->StorageId()]) ? $StorageById[$event->StorageId()]->Name() : 'Unknown Storage Id: '.$event->StorageId();
} else {
echo 'Default';
}
if ( $event->SecondaryStorageId() ) {
echo '<br/>'.(isset($StorageById[$event->SecondaryStorageId()]) ? $StorageById[$event->SecondaryStorageId()]->Name() : 'Unknown Storage Id '.$event->SecondaryStorageId());
}
?>
</td>
<?php
}
if ( ZM_WEB_EVENT_DISK_SPACE ) {
$disk_space_total += $event->DiskSpace();
?>
<td class="colDiskSpace"><?php echo human_filesize($event->DiskSpace()) ?></td>
<?php
}
if ( ZM_WEB_LIST_THUMBS ) {
echo '<td class="colThumbnail zoom">';
$imgSrc = $event->getThumbnailSrc(array(),'&amp;');
$streamSrc = $event->getStreamSrc(array(
'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single', 'rate'=>'400'), '&amp;');
$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.'"/>';
echo '<a href="?view=event&amp;eid='. $event->Id().$filterQuery.$sortQuery.'&amp;page=1">'.$imgHtml.'</a>';
echo '</td>';
} // end if ZM_WEB_LIST_THUMBS
?>
</tr>
<?php
} # end foreach row
?>
</tbody> </tbody>
<?php
} # end if $results
if ( ZM_WEB_EVENT_DISK_SPACE ) {
?>
<tfoot>
<tr>
<td colspan="11">Totals:</td>
<?php
if ( count($storage_areas)>1 ) {
?>
<td class="colStorage"></td>
<?php
}
?>
<td class="colDiskSpace"><?php echo human_filesize($disk_space_total) ?></td>
<?php
if ( ZM_WEB_LIST_THUMBS ) {
?>
<td></td>
<?php
}
?>
<td></td>
</tr>
</tfoot>
<?php
}
?>
</table> </table>
</div> </div>
</div> </div>

View File

@ -485,6 +485,10 @@ if ( ZM_OPT_MESSAGE ) {
<label for="Concurrent"><?php echo translate('ConcurrentFilter') ?></label> <label for="Concurrent"><?php echo translate('ConcurrentFilter') ?></label>
<input type="checkbox" id="filter[Concurrent]" name="filter[Concurrent]" value="1"<?php if ( $filter->Concurrent() ) { ?> checked="checked"<?php } ?> data-on-click-this="updateButtons"/> <input type="checkbox" id="filter[Concurrent]" name="filter[Concurrent]" value="1"<?php if ( $filter->Concurrent() ) { ?> checked="checked"<?php } ?> data-on-click-this="updateButtons"/>
</p> </p>
<p>
<label for="LockRows"><?php echo translate('FilterLockRows') ?></label>
<input type="checkbox" id="filter[LockRows]" name="filter[LockRows]" value="1"<?php if ( $filter->LockRows() ) { ?> checked="checked"<?php } ?> data-on-click-this="updateButtons"/>
</p>
</div> </div>
<hr/> <hr/>
<div id="contentButtons"> <div id="contentButtons">

View File

@ -61,7 +61,7 @@ function processRows(rows) {
if ( canEditMonitors ) row.Monitor = '<a href="?view=monitor&amp;mid=' + mid + '">' + row.Monitor + '</a>'; 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 ( canEditEvents ) row.Cause = '<a href="#" title="' + row.Notes + '" class="eDetailLink" data-eid="' + eid + '">' + row.Cause + '</a>';
if ( row.Notes.indexOf('detected:') >= 0 ) { 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>'; 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: ' ) { } else if ( row.Notes != 'Forced Web: ' ) {
row.Cause = row.Cause + '<br/><div class="small text-nowrap text-muted">' + row.Notes + '</div>'; row.Cause = row.Cause + '<br/><div class="small text-nowrap text-muted">' + row.Notes + '</div>';
} }
@ -135,7 +135,7 @@ function manageDelConfirmModalBtns() {
$j.getJSON(thisUrl + '?request=events&task=delete&eids[]='+selections.join('&eids[]=')) $j.getJSON(thisUrl + '?request=events&task=delete&eids[]='+selections.join('&eids[]='))
.done( function(data) { .done( function(data) {
$j('#eventTable').bootstrapTable('refresh'); $j('#eventTable').bootstrapTable('refresh');
window.location.reload(true); $j('#deleteConfirm').modal('hide');
}) })
.fail(logAjaxFail); .fail(logAjaxFail);
}); });
@ -238,7 +238,6 @@ function initPage() {
$j.getJSON(thisUrl + '?request=events&task=archive&eids[]='+selections.join('&eids[]=')) $j.getJSON(thisUrl + '?request=events&task=archive&eids[]='+selections.join('&eids[]='))
.done( function(data) { .done( function(data) {
$j('#eventTable').bootstrapTable('refresh'); $j('#eventTable').bootstrapTable('refresh');
window.location.reload(true);
}) })
.fail(logAjaxFail); .fail(logAjaxFail);
}); });
@ -257,11 +256,8 @@ function initPage() {
$j.getJSON(thisUrl + '?request=events&task=unarchive&eids[]='+selections.join('&eids[]=')) $j.getJSON(thisUrl + '?request=events&task=unarchive&eids[]='+selections.join('&eids[]='))
.done( function(data) { .done( function(data) {
$j('#eventTable').bootstrapTable('refresh'); $j('#eventTable').bootstrapTable('refresh');
window.location.reload(true);
}) })
.fail(logAjaxFail); .fail(logAjaxFail);
//window.location.reload(true);
}); });
// Manage the EDIT button // Manage the EDIT button
@ -321,13 +317,6 @@ function initPage() {
$j('#deleteConfirm').modal('show'); $j('#deleteConfirm').modal('show');
}); });
// Manage the eventdetail links in the events list
$j(".eDetailLink").click(function(evt) {
evt.preventDefault();
var eid = $j(this).data('eid');
getEventDetailModal(eid);
});
// Update table links each time after new data is loaded // Update table links each time after new data is loaded
table.on('post-body.bs.table', function(data) { table.on('post-body.bs.table', function(data) {
// Manage the eventdetail links in the events list // Manage the eventdetail links in the events list
@ -343,6 +332,7 @@ function initPage() {
table.find("tr td:nth-child(" + (thumb_ndx+1) + ")").addClass('colThumbnail zoom'); table.find("tr td:nth-child(" + (thumb_ndx+1) + ")").addClass('colThumbnail zoom');
}); });
table.bootstrapTable('resetSearch');
// The table is initially given a hidden style, so now that we are done rendering, show it // The table is initially given a hidden style, so now that we are done rendering, show it
table.show(); table.show();
} }

View File

@ -74,9 +74,7 @@ function validateForm(form) {
function updateButtons(element) { function updateButtons(element) {
var form = element.form; var form = element.form;
if ( element.type == 'checkbox' && element.checked ) {
form.elements['executeButton'].disabled = false;
} else {
var canExecute = false; var canExecute = false;
if ( form.elements['filter[AutoArchive]'] && form.elements['filter[AutoArchive]'].checked ) { if ( form.elements['filter[AutoArchive]'] && form.elements['filter[AutoArchive]'].checked ) {
canExecute = true; canExecute = true;
@ -102,7 +100,6 @@ function updateButtons(element) {
canExecute = true; canExecute = true;
} }
form.elements['executeButton'].disabled = !canExecute; form.elements['executeButton'].disabled = !canExecute;
}
if ( form.elements['filter[Name]'].value ) { if ( form.elements['filter[Name]'].value ) {
form.elements['Save'].disabled = false; form.elements['Save'].disabled = false;
form.elements['SaveAs'].disabled = false; form.elements['SaveAs'].disabled = false;

View File

@ -1,4 +1,4 @@
var ZM_OPT_USE_GEOLOCATION = '<?php echo ZM_OPT_USE_GEOLOCATION ?>'; var ZM_OPT_USE_GEOLOCATION = '<?php echo ZM_OPT_USE_GEOLOCATION ?>' == '1' ? true : false;
<?php <?php
if ( ZM_OPT_USE_GEOLOCATION ) { if ( ZM_OPT_USE_GEOLOCATION ) {
echo 'var ZM_OPT_GEOLOCATION_TILE_PROVIDER=\''.ZM_OPT_GEOLOCATION_TILE_PROVIDER.'\''.PHP_EOL; echo 'var ZM_OPT_GEOLOCATION_TILE_PROVIDER=\''.ZM_OPT_GEOLOCATION_TILE_PROVIDER.'\''.PHP_EOL;

View File

@ -1,128 +0,0 @@
<?php
//
// ZoneMinder web events view file, $Date$, $Revision$
// Copyright (C) 2001-2008 Philip Coombes
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
if ( !canView('Events') || (!empty($_REQUEST['execute']) && !canEdit('Events')) ) {
$view = 'error';
return;
}
require_once('includes/Event.php');
require_once('includes/Filter.php');
$eventsSql = 'SELECT E.*,M.Name AS MonitorName,M.DefaultScale FROM Monitors AS M INNER JOIN Events AS E on (M.Id = E.MonitorId) WHERE';
if ( $user['MonitorIds'] ) {
$user_monitor_ids = ' M.Id in ('.$user['MonitorIds'].')';
$eventsSql .= $user_monitor_ids;
} else {
$eventsSql .= ' 1';
}
$filter = isset($_REQUEST['filter_id']) ? new ZM\Filter($_REQUEST['filter_id']) : new ZM\Filter();
if ( isset($_REQUEST['filter'])) {
$filter->set($_REQUEST['filter']);
}
parseSort();
$filterQuery = $filter->querystring();
xhtmlHeaders(__FILE__, translate('Events'));
getBodyTopHTML();
?>
<?php echo getNavBarHTML() ?>
<div id="page" class="container-fluid p-3">
<!-- Toolbar button placement and styling handled by bootstrap-tables -->
<div id="toolbar">
<button id="backBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Back') ?>" disabled><i class="fa fa-arrow-left"></i></button>
<button id="refreshBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Refresh') ?>" ><i class="fa fa-refresh"></i></button>
<button id="tlineBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('ShowTimeline') ?>" ><i class="fa fa-history"></i></button>
<button id="filterBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Filter') ?>"><i class="fa fa-filter"></i></button>
<button id="viewBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('View') ?>" disabled><i class="fa fa-binoculars"></i></button>
<button id="archiveBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Archive') ?>" disabled><i class="fa fa-archive"></i></button>
<button id="unarchiveBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Unarchive') ?>" disabled><i class="fa fa-file-archive-o"></i></button>
<button id="editBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Edit') ?>" disabled><i class="fa fa-pencil"></i></button>
<button id="exportBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Export') ?>" disabled><i class="fa fa-external-link"></i></button>
<button id="downloadBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('DownloadVideo') ?>" disabled><i class="fa fa-download"></i></button>
<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>
<!-- Table styling handled by bootstrap-tables -->
<div class="row justify-content-center">
<table
id="eventTable"
data-locale="<?php echo i18n() ?>"
data-side-pagination="server"
data-ajax="ajaxRequest"
data-pagination="true"
data-show-pagination-switch="true"
data-page-list="[10, 25, 50, 100, 200, All]"
data-search="true"
data-cookie="true"
data-cookie-id-table="zmEventsTable"
data-cookie-expire="2y"
data-click-to-select="true"
data-remember-order="true"
data-show-columns="true"
data-show-export="true"
data-uncheckAll="true"
data-toolbar="#toolbar"
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"
class="table-sm table-borderless"
style="display:none;"
>
<thead>
<!-- Row styling is handled by bootstrap-tables -->
<tr>
<th data-sortable="false" data-field="toggleCheck" data-checkbox="true"></th>
<th data-sortable="true" data-field="Id"><?php echo translate('Id') ?></th>
<th data-sortable="true" data-field="Name"><?php echo translate('Name') ?></th>
<th data-sortable="true" data-field="Archived"><?php echo translate('Archived') ?></th>
<th data-sortable="true" data-field="Emailed"><?php echo translate('Emailed') ?></th>
<th data-sortable="true" data-field="Monitor"><?php echo translate('Monitor') ?></th>
<th data-sortable="true" data-field="Cause"><?php echo translate('Cause') ?></th>
<th data-sortable="true" data-field="StartTime"><?php echo translate('AttrStartTime') ?></th>
<th data-sortable="true" data-field="EndTime"><?php echo translate('AttrEndTime') ?></th>
<th data-sortable="true" data-field="Length"><?php echo translate('Duration') ?></th>
<th data-sortable="true" data-field="Frames"><?php echo translate('Frames') ?></th>
<th data-sortable="true" data-field="AlarmFrames"><?php echo translate('AlarmBrFrames') ?></th>
<th data-sortable="true" data-field="TotScore"><?php echo translate('TotalBrScore') ?></th>
<th data-sortable="true" data-field="AvgScore"><?php echo translate('AvgBrScore') ?></th>
<th data-sortable="true" data-field="MaxScore"><?php echo translate('MaxBrScore') ?></th>
<th data-sortable="false" data-field="Storage"><?php echo translate('Storage') ?></th>
<th data-sortable="true" data-field="DiskSpace"><?php echo translate('DiskSpace') ?></th>
<th data-sortable="false" data-field="Thumbnail"><?php echo translate('Thumbnail') ?></th>
</tr>
</thead>
<tbody>
<!-- Row data populated via Ajax -->
</tbody>
</table>
</div>
</div>
<?php xhtmlFooter() ?>

View File

@ -279,6 +279,12 @@ foreach ( array_map('basename', glob('skins/'.$skin.'/css/*', GLOB_ONLYDIR)) as
<tbody> <tbody>
<?php <?php
foreach( ZM\Storage::find( null, array('order'=>'lower(Name)') ) as $Storage ) { foreach( ZM\Storage::find( null, array('order'=>'lower(Name)') ) as $Storage ) {
$filter = new ZM\Filter();
$filter->addTerm(array('attr'=>'StorageId','op'=>'=','val'=>$Storage->Id()));
if ( $user['MonitorIds'] ) {
$filter = $filter->addTerm(array('cnj'=>'and', 'attr'=>'MonitorId', 'op'=>'IN', 'val'=>$user['MonitorIds']));
}
$str_opt = 'class="storageCol" data-sid="'.$Storage->Id().'"'; $str_opt = 'class="storageCol" data-sid="'.$Storage->Id().'"';
?> ?>
<tr> <tr>
@ -289,7 +295,7 @@ foreach ( array_map('basename', glob('skins/'.$skin.'/css/*', GLOB_ONLYDIR)) as
<td class="colScheme"><?php echo makeLink('#', validHtmlStr($Storage->Scheme()), $canEdit, $str_opt) ?></td> <td class="colScheme"><?php echo makeLink('#', validHtmlStr($Storage->Scheme()), $canEdit, $str_opt) ?></td>
<td class="colServer"><?php echo makeLink('#', validHtmlStr($Storage->Server()->Name()), $canEdit, $str_opt) ?></td> <td class="colServer"><?php echo makeLink('#', validHtmlStr($Storage->Server()->Name()), $canEdit, $str_opt) ?></td>
<td class="colDiskSpace"><?php echo human_filesize($Storage->disk_used_space()) . ' of ' . human_filesize($Storage->disk_total_space()) ?></td> <td class="colDiskSpace"><?php echo human_filesize($Storage->disk_used_space()) . ' of ' . human_filesize($Storage->disk_total_space()) ?></td>
<td class="ColEvents"><?php echo $Storage->EventCount().' using '.human_filesize($Storage->event_disk_space()) ?></td> <td class="ColEvents"><?php echo makeLink('?view=events'.$filter->querystring(), $Storage->EventCount().' using '.human_filesize($Storage->event_disk_space()) ); ?></td>
<td class="colMark"><input type="checkbox" name="markIds[]" value="<?php echo $Storage->Id() ?>" data-on-click-this="configureDeleteButton"<?php if ( $Storage->EventCount() or !$canEdit ) { ?> disabled="disabled"<?php } ?><?php echo $Storage->EventCount() ? ' title="Can\'t delete as long as there are events stored here."' : ''?>/></td> <td class="colMark"><input type="checkbox" name="markIds[]" value="<?php echo $Storage->Id() ?>" data-on-click-this="configureDeleteButton"<?php if ( $Storage->EventCount() or !$canEdit ) { ?> disabled="disabled"<?php } ?><?php echo $Storage->EventCount() ? ' title="Can\'t delete as long as there are events stored here."' : ''?>/></td>
</tr> </tr>
<?php } #end foreach Server ?> <?php } #end foreach Server ?>
@ -304,11 +310,10 @@ foreach ( array_map('basename', glob('skins/'.$skin.'/css/*', GLOB_ONLYDIR)) as
<?php <?php
} else if ( $tab == 'API' ) { } else if ( $tab == 'API' ) {
$apiEnabled = dbFetchOne("SELECT Value FROM Config WHERE Name='ZM_OPT_USE_API'"); $apiEnabled = dbFetchOne('SELECT Value FROM Config WHERE Name=\'ZM_OPT_USE_API\'');
if ( $apiEnabled['Value'] != '1' ) { if ( $apiEnabled['Value'] != '1' ) {
echo "<div class='errorText'>APIs are disabled. To enable, please turn on OPT_USE_API in Options->System</div>"; echo '<div class="errorText">APIs are disabled. To enable, please turn on OPT_USE_API in Options->System</div>';
} } else {
else {
?> ?>
<form name="userForm" method="post" action="?"> <form name="userForm" method="post" action="?">