Merge branch 'master' into fix_token_auth_sessions

This commit is contained in:
Isaac Connor 2019-08-12 13:59:11 -04:00
commit 4922861d1d
35 changed files with 892 additions and 731 deletions

View File

@ -186,6 +186,7 @@ CREATE TABLE `Events` (
`Id` bigint unsigned NOT NULL auto_increment,
`MonitorId` int(10) unsigned NOT NULL default '0',
`StorageId` smallint(5) unsigned default 0,
`SecondaryStorageId` smallint(5) unsigned default 0,
`Name` varchar(64) NOT NULL default '',
`Cause` varchar(32) NOT NULL default '',
`StartTime` datetime default NULL,

51
db/zm_update-1.33.14.sql Normal file
View File

@ -0,0 +1,51 @@
--
-- Add CopyTo action to Filters
--
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Filters'
AND column_name = 'AutoCopy'
) > 0,
"SELECT 'Column AutoCopy already exists in Filters'",
"ALTER TABLE Filters ADD `AutoCopy` tinyint(3) unsigned NOT NULL default '0' AFTER `AutoMove`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Filters'
AND column_name = 'AutoCopyTo'
) > 0,
"SELECT 'Column AutoCopyTo already exists in Filters'",
"ALTER TABLE Filters ADD `AutoCopyTo` smallint(5) unsigned NOT NULL default '0' AFTER `AutoCopy`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Filters'
AND column_name = 'Query_json'
) > 0,
"SELECT 'Column Query_json already exists in Filters'",
"ALTER TABLE `Filters` Change `Query` `Query_json` text NOT NULL"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Events'
AND column_name = 'SecondaryStorageId'
) > 0,
"SELECT 'Column SecondaryStorageId already exists in Events'",
"ALTER TABLE `Events` ADD `SecondaryStorageId` smallint(5) unsigned default 0 AFTER `StorageId`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

View File

@ -23,7 +23,7 @@
%global _hardened_build 1
Name: zoneminder
Version: 1.33.12
Version: 1.33.14
Release: 1%{?dist}
Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons
@ -411,6 +411,9 @@ EOF
%dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload
%changelog
* Sun Aug 11 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.14-1
- Bump to 1.33.13 Development
* Sun Jul 07 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.12-1
- Bump to 1.33.12 Development

View File

@ -646,6 +646,23 @@ Why am I getting broken images when trying to view events?
Zoneminder and the Apache web server need to have the right permissions. Check this forum topic and similar ones:
http://www.zoneminder.com/forums/viewtopic.php?p=48754#48754
I can review events for the current day, but ones from yesterday and beyond error out
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you've checked that the `www-data` user has permissions to the storage folders, perhaps your php.ini's timezone setting is incorrect. They _must_ match for certain playback functions.
If you're using Linux, this can be found using the following command: ::
timedatectl | grep "Time zone"
If using FreeBSD, you can use this one-liner: ::
cd /usr/share/zoneinfo/ && find * -type f -exec cmp -s {} /etc/localtime \; -print;
Once you know what timezone your system is set to, open `/etc/php.ini` and adjust ``date.timezone`` to the appropriate value. the PHP daemon may need to be restarted for changes to take effect.
Why is the image from my color camera appearing in black and white?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you recently upgraded to zoneminder 1.26, there is a per camera option that defaults to black and white and can be mis-set if your upgrade didn't happen right. See this thread: http://www.zoneminder.com/forums/viewtopic.php?f=30&t=21344
@ -728,6 +745,8 @@ What causes "Invalid JPEG file structure: two SOI markers" from zmc (1.24.x)
Some settings that used to be global only are now per camera. On the Monitor Source tab, if you are using Remote Protocol "HTTP" and Remote Method "Simple", try changing Remote Method to "Regexp".
Miscellaneous
-------------------
I see ZoneMinder is licensed under the GPL. What does that allow or restrict me in doing with ZoneMinder?

View File

@ -189,11 +189,17 @@ Add the following to the bottom of the file
::
# Backports repository
deb http://httpredir.debian.org/debian jessie-backports main contrib non-free
deb http://archive.debian.org/debian/ jessie-backports main contrib non-free
CTRL+o and <Enter> to save
CTRL+x to exit
Run the following
::
echo 'Acquire::Check-Valid-Until no;' > /etc/apt/apt.conf.d/99no-check-valid-until
**Step 5:** Install ZoneMinder
::

View File

@ -41,6 +41,7 @@ require Number::Bytes::Human;
require Date::Parse;
require POSIX;
use Date::Format qw(time2str);
use Time::HiRes qw(gettimeofday tv_interval);
#our @ISA = qw(ZoneMinder::Object);
use parent qw(ZoneMinder::Object);
@ -63,6 +64,7 @@ $serial = $primary_key = 'Id';
Id
MonitorId
StorageId
SecondaryStorageId
Name
Cause
StartTime
@ -116,7 +118,7 @@ sub Time {
}
sub getPath {
return Path( @_ );
return Path(@_);
}
sub Path {
@ -131,7 +133,7 @@ sub Path {
if ( ! $$event{Path} ) {
my $Storage = $event->Storage();
$$event{Path} = join('/', $Storage->Path(), $event->RelativePath() );
$$event{Path} = join('/', $Storage->Path(), $event->RelativePath());
}
return $$event{Path};
}
@ -163,7 +165,8 @@ sub RelativePath {
if ( $event->Time() ) {
$$event{RelativePath} = join('/',
$event->{MonitorId},
POSIX::strftime( '%y/%m/%d/%H/%M/%S',
POSIX::strftime(
'%y/%m/%d/%H/%M/%S',
localtime($event->Time())
),
);
@ -203,7 +206,8 @@ sub LinkPath {
if ( $event->Time() ) {
$$event{LinkPath} = join('/',
$event->{MonitorId},
POSIX::strftime( '%y/%m/%d',
POSIX::strftime(
'%y/%m/%d',
localtime($event->Time())
),
'.'.$$event{Id}
@ -255,8 +259,8 @@ sub createIdFile {
sub GenerateVideo {
my ( $self, $rate, $fps, $scale, $size, $overwrite, $format ) = @_;
my $event_path = $self->Path( );
chdir( $event_path );
my $event_path = $self->Path();
chdir($event_path);
( my $video_name = $self->{Name} ) =~ s/\s/_/g;
my @file_parts;
@ -282,10 +286,10 @@ sub GenerateVideo {
$file_scale =~ s/_00//;
$file_scale =~ s/(_\d+)0+$/$1/;
$file_scale = 's'.$file_scale;
push( @file_parts, $file_scale );
push @file_parts, $file_scale;
} elsif ( $size ) {
my $file_size = 'S'.$size;
push( @file_parts, $file_size );
push @file_parts, $file_size;
}
my $video_file = join('-', $video_name, $file_parts[0], $file_parts[1] ).'.'.$format;
if ( $overwrite || !-s $video_file ) {
@ -393,61 +397,66 @@ sub delete {
sub delete_files {
my $event = shift;
my $Storage = @_ ? $_[0] : new ZoneMinder::Storage($$event{StorageId});
my $storage_path = $Storage->Path();
foreach my $Storage (
@_ ? ($_[0]) : (
new ZoneMinder::Storage($$event{StorageId}),
( $$event{SecondaryStorageId} ? new ZoneMinder::Storage($$event{SecondaryStorageId}) : () ),
) ) {
my $storage_path = $Storage->Path();
if ( ! $storage_path ) {
Error("Empty storage path when deleting files for event $$event{Id} with storage id $$event{StorageId}");
return;
}
if ( ! $$event{MonitorId} ) {
Error("No monitor id assigned to event $$event{Id}");
return;
}
my $event_path = $event->RelativePath();
Debug("Deleting files for Event $$event{Id} from $storage_path/$event_path, scheme is $$event{Scheme}.");
if ( $event_path ) {
( $storage_path ) = ( $storage_path =~ /^(.*)$/ ); # De-taint
( $event_path ) = ( $event_path =~ /^(.*)$/ ); # De-taint
my $deleted = 0;
if ( $$Storage{Type} and ( $$Storage{Type} eq 's3fs' ) ) {
my ( $aws_id, $aws_secret, $aws_host, $aws_bucket ) = ( $$Storage{Url} =~ /^\s*([^:]+):([^@]+)@([^\/]*)\/(.+)\s*$/ );
eval {
require Net::Amazon::S3;
my $s3 = Net::Amazon::S3->new( {
aws_access_key_id => $aws_id,
aws_secret_access_key => $aws_secret,
( $aws_host ? ( host => $aws_host ) : () ),
});
my $bucket = $s3->bucket($aws_bucket);
if ( ! $bucket ) {
Error("S3 bucket $bucket not found.");
die;
}
if ( $bucket->delete_key($event_path) ) {
$deleted = 1;
} else {
Error('Failed to delete from S3:'.$s3->err . ': ' . $s3->errstr);
}
};
Error($@) if $@;
if ( ! $storage_path ) {
Error("Empty storage path when deleting files for event $$event{Id} with storage id $$event{StorageId}");
return;
}
if ( !$deleted ) {
my $command = "/bin/rm -rf $storage_path/$event_path";
ZoneMinder::General::executeShellCommand($command);
}
}
if ( $event->Scheme() eq 'Deep' ) {
my $link_path = $event->LinkPath();
Debug("Deleting link for Event $$event{Id} from $storage_path/$link_path.");
if ( $link_path ) {
( $link_path ) = ( $link_path =~ /^(.*)$/ ); # De-taint
if ( ! $$event{MonitorId} ) {
Error("No monitor id assigned to event $$event{Id}");
return;
}
my $event_path = $event->RelativePath();
Debug("Deleting files for Event $$event{Id} from $storage_path/$event_path, scheme is $$event{Scheme}.");
if ( $event_path ) {
( $storage_path ) = ( $storage_path =~ /^(.*)$/ ); # De-taint
( $event_path ) = ( $event_path =~ /^(.*)$/ ); # De-taint
my $deleted = 0;
if ( $$Storage{Type} and ( $$Storage{Type} eq 's3fs' ) ) {
my ( $aws_id, $aws_secret, $aws_host, $aws_bucket ) = ( $$Storage{Url} =~ /^\s*([^:]+):([^@]+)@([^\/]*)\/(.+)\s*$/ );
eval {
require Net::Amazon::S3;
my $s3 = Net::Amazon::S3->new( {
aws_access_key_id => $aws_id,
aws_secret_access_key => $aws_secret,
( $aws_host ? ( host => $aws_host ) : () ),
});
my $bucket = $s3->bucket($aws_bucket);
if ( ! $bucket ) {
Error("S3 bucket $bucket not found.");
die;
}
if ( $bucket->delete_key($event_path) ) {
$deleted = 1;
} else {
Error('Failed to delete from S3:'.$s3->err . ': ' . $s3->errstr);
}
};
Error($@) if $@;
}
if ( !$deleted ) {
my $command = "/bin/rm -rf $storage_path/$event_path";
ZoneMinder::General::executeShellCommand($command);
}
} # end if event_path
if ( $event->Scheme() eq 'Deep' ) {
my $link_path = $event->LinkPath();
Debug("Deleting link for Event $$event{Id} from $storage_path/$link_path.");
if ( $link_path ) {
( $link_path ) = ( $link_path =~ /^(.*)$/ ); # De-taint
unlink($storage_path.'/'.$link_path) or Error("Unable to unlink '$storage_path/$link_path': $!");
}
}
}
} # end if Scheme eq Deep
} # end foreach Storage
} # end sub delete_files
sub StorageId {
@ -519,7 +528,7 @@ sub DiskSpace {
return $_[0]{DiskSpace};
}
sub MoveTo {
sub CopyTo {
my ( $self, $NewStorage ) = @_;
my $OldStorage = $self->Storage(undef);
@ -531,9 +540,9 @@ sub MoveTo {
# We do this before bothering to lock the event
my ( $NewPath ) = ( $NewStorage->Path() =~ /^(.*)$/ ); # De-taint
if ( ! $$NewStorage{Id} ) {
return "New storage does not have an id. Moving will not happen.";
return 'New storage does not have an id. Moving will not happen.';
} elsif ( $$NewStorage{Id} == $$self{StorageId} ) {
return "Event is already located at " . $NewPath;
return 'Event is already located at ' . $NewPath;
} elsif ( !$NewPath ) {
return "New path ($NewPath) is empty.";
} elsif ( ! -e $NewPath ) {
@ -545,7 +554,7 @@ sub MoveTo {
# data is reloaded, so need to check that the move hasn't already happened.
if ( $$self{StorageId} == $$NewStorage{Id} ) {
$ZoneMinder::Database::dbh->commit();
return "Event has already been moved by someone else.";
return 'Event has already been moved by someone else.';
}
if ( $$OldStorage{Id} != $$self{StorageId} ) {
@ -559,70 +568,76 @@ sub MoveTo {
$ZoneMinder::Database::dbh->commit();
return "New path and old path are the same! $NewPath";
}
Debug("Moving event $$self{Id} from $OldPath to $NewPath");
Debug("Copying event $$self{Id} from $OldPath to $NewPath");
my $moved = 0;
if ( $$NewStorage{Type} eq 's3fs' ) {
my ( $aws_id, $aws_secret, $aws_host, $aws_bucket ) = ( $$NewStorage{Url} =~ /^\s*([^:]+):([^@]+)@([^\/]*)\/(.+)\s*$/ );
eval {
require Net::Amazon::S3;
require File::Slurp;
my $s3 = Net::Amazon::S3->new( {
aws_access_key_id => $aws_id,
aws_secret_access_key => $aws_secret,
( $aws_host ? ( host => $aws_host ) : () ),
});
my $bucket = $s3->bucket($aws_bucket);
if ( ! $bucket ) {
Error("S3 bucket $bucket not found.");
die;
if ( $$NewStorage{Url} ) {
my ( $aws_id, $aws_secret, $aws_host, $aws_bucket ) = ( $$NewStorage{Url} =~ /^\s*([^:]+):([^@]+)@([^\/]*)\/(.+)\s*$/ );
if ( $aws_id and $aws_secret and $aws_host and $aws_bucket ) {
eval {
require Net::Amazon::S3;
require File::Slurp;
my $s3 = Net::Amazon::S3->new( {
aws_access_key_id => $aws_id,
aws_secret_access_key => $aws_secret,
( $aws_host ? ( host => $aws_host ) : () ),
});
my $bucket = $s3->bucket($aws_bucket);
if ( !$bucket ) {
Error("S3 bucket $bucket not found.");
die;
}
my $event_path = $self->RelativePath();
Debug("Making directory $event_path/");
if ( ! $bucket->add_key($event_path.'/', '') ) {
die "Unable to add key for $event_path/";
}
my @files = glob("$OldPath/*");
Debug("Files to move @files");
foreach my $file ( @files ) {
next if $file =~ /^\./;
( $file ) = ( $file =~ /^(.*)$/ ); # De-taint
my $starttime = [gettimeofday];
Debug("Moving file $file to $NewPath");
my $size = -s $file;
if ( ! $size ) {
Info('Not moving file with 0 size');
}
my $file_contents = File::Slurp::read_file($file);
if ( ! $file_contents ) {
die 'Loaded empty file, but it had a size. Giving up';
}
my $filename = $event_path.'/'.File::Basename::basename($file);
if ( ! $bucket->add_key($filename, $file_contents) ) {
die "Unable to add key for $filename";
}
my $duration = tv_interval($starttime);
Debug('PUT to S3 ' . Number::Bytes::Human::format_bytes($size) . " in $duration seconds = " . Number::Bytes::Human::format_bytes($duration?$size/$duration:$size) . '/sec');
} # end foreach file.
$moved = 1;
};
Error($@) if $@;
} else {
Error("Unable to parse S3 Url into it's component parts.");
}
my $event_path = 'events/'.$self->RelativePath();
Info("Making dir ectory $event_path/");
if ( ! $bucket->add_key( $event_path.'/','' ) ) {
die "Unable to add key for $event_path/";
}
my @files = glob("$OldPath/*");
Debug("Files to move @files");
for my $file (@files) {
next if $file =~ /^\./;
( $file ) = ( $file =~ /^(.*)$/ ); # De-taint
my $starttime = time;
Debug("Moving file $file to $NewPath");
my $size = -s $file;
if ( ! $size ) {
Info('Not moving file with 0 size');
}
my $file_contents = File::Slurp::read_file($file);
if ( ! $file_contents ) {
die 'Loaded empty file, but it had a size. Giving up';
}
my $filename = $event_path.'/'.File::Basename::basename($file);
if ( ! $bucket->add_key( $filename, $file_contents ) ) {
die "Unable to add key for $filename";
}
my $duration = time - $starttime;
Debug('PUT to S3 ' . Number::Bytes::Human::format_bytes($size) . " in $duration seconds = " . Number::Bytes::Human::format_bytes($duration?$size/$duration:$size) . '/sec');
} # end foreach file.
$moved = 1;
};
Error($@) if $@;
die $@ if $@;
#die $@ if $@;
} # end if Url
} # end if s3
my $error = '';
if ( ! $moved ) {
File::Path::make_path( $NewPath, {error => \my $err} );
if ( !$moved ) {
File::Path::make_path($NewPath, {error => \my $err});
if ( @$err ) {
for my $diag (@$err) {
my ($file, $message) = %$diag;
next if $message eq 'File exists';
if ($file eq '') {
if ( $file eq '' ) {
$error .= "general error: $message\n";
} else {
$error .= "problem making $file: $message\n";
@ -636,21 +651,21 @@ Debug("Files to move @files");
my @files = glob("$OldPath/*");
if ( ! @files ) {
$ZoneMinder::Database::dbh->commit();
return "No files to move.";
return 'No files to move.';
}
for my $file (@files) {
next if $file =~ /^\./;
( $file ) = ( $file =~ /^(.*)$/ ); # De-taint
my $starttime = time;
my $starttime = [gettimeofday];
Debug("Moving file $file to $NewPath");
my $size = -s $file;
if ( ! File::Copy::copy( $file, $NewPath ) ) {
$error .= "Copy failed: for $file to $NewPath: $!";
last;
}
my $duration = time - $starttime;
Debug("Copied " . Number::Bytes::Human::format_bytes($size) . " in $duration seconds = " . ($duration?Number::Bytes::Human::format_bytes($size/$duration):'inf') . "/sec");
my $duration = tv_interval($starttime);
Debug('Copied ' . Number::Bytes::Human::format_bytes($size) . " in $duration seconds = " . ($duration?Number::Bytes::Human::format_bytes($size/$duration):'inf') . '/sec');
} # end foreach file.
} # end if ! moved
@ -658,6 +673,15 @@ Debug("Files to move @files");
$ZoneMinder::Database::dbh->commit();
return $error;
}
} # end sub CopyTo
sub MoveTo {
my ( $self, $NewStorage ) = @_;
my $OldStorage = $self->Storage(undef);
my $error = $self->CopyTo($NewStorage);
return $error if $error;
# Succeeded in copying all files, so we may now update the Event.
$$self{StorageId} = $$NewStorage{Id};
@ -667,10 +691,8 @@ Debug("Files to move @files");
$ZoneMinder::Database::dbh->commit();
return $error;
}
Debug("Committing");
$ZoneMinder::Database::dbh->commit();
$self->delete_files( $OldStorage );
Debug("Done deleting files, returning");
$self->delete_files($OldStorage);
return $error;
} # end sub MoveTo

View File

@ -132,11 +132,13 @@ sub Sql {
my $self = shift;
$$self{Sql} = shift if @_;
if ( ! $$self{Sql} ) {
if ( !$self->{Query} ) {
Warning('No Query in filter.');
$self->{Sql} = '';
if ( ! $self->{Query_json} ) {
Warning("No query in Filter!");
return;
}
my $filter_expr = ZoneMinder::General::jsonDecode($self->{Query});
my $filter_expr = ZoneMinder::General::jsonDecode($self->{Query_json});
my $sql = 'SELECT E.*,
unix_timestamp(E.StartTime) as Time,
M.Name as MonitorName,
@ -146,7 +148,6 @@ sub Sql {
INNER JOIN Monitors as M on M.Id = E.MonitorId
LEFT JOIN Storage as S on S.Id = E.StorageId
';
$self->{Sql} = '';
if ( $filter_expr->{terms} ) {
foreach my $term ( @{$filter_expr->{terms}} ) {

View File

@ -46,56 +46,13 @@ use ZoneMinder::Database qw(:all);
use POSIX;
use vars qw/ $table $primary_key /;
use vars qw/ $table $primary_key %fields/;
$table = 'Storage';
$primary_key = 'Id';
#__PACKAGE__->table('Storage');
#__PACKAGE__->primary_key('Id');
%fields = map { $_ => $_ } qw( Id Name Path DoDelete ServerId Type Url DiskSpace Scheme );
sub find {
shift if $_[0] eq 'ZoneMinder::Storage';
my %sql_filters = @_;
my $sql = 'SELECT * FROM Storage';
my @sql_filters;
my @sql_values;
if ( exists $sql_filters{Id} ) {
push @sql_filters , ' Id=? ';
push @sql_values, $sql_filters{Id};
}
if ( exists $sql_filters{Name} ) {
push @sql_filters , ' Name = ? ';
push @sql_values, $sql_filters{Name};
}
if ( exists $sql_filters{ServerId} ) {
push @sql_filters, ' ServerId = ?';
push @sql_values, $sql_filters{ServerId};
}
$sql .= ' WHERE ' . join(' AND ', @sql_filters) if @sql_filters;
$sql .= ' LIMIT ' . $sql_filters{limit} if $sql_filters{limit};
my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() );
my $res = $sth->execute( @sql_values )
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
my @results;
while( my $db_filter = $sth->fetchrow_hashref() ) {
my $filter = new ZoneMinder::Storage( $$db_filter{Id}, $db_filter );
push @results, $filter;
} # end while
Debug("SQL: $sql returned " . @results . ' results');
return @results;
}
sub find_one {
my @results = find(@_);
return $results[0] if @results;
}
sub Path {
if ( @_ > 1 ) {

View File

@ -240,6 +240,7 @@ sub getFilters {
or AutoDelete = 1
or UpdateDiskSpace = 1
or AutoMove = 1
or AutoCopy = 1
) ORDER BY Name';
my $sth = $dbh->prepare_cached($sql)
or Fatal("Unable to prepare '$sql': ".$dbh->errstr());
@ -283,6 +284,7 @@ sub checkFilter {
($filter->{AutoMessage}?'message':()),
($filter->{AutoExecute}?'execute':()),
($filter->{AutoMove}?'move':()),
($filter->{AutoCopy}?'copy':()),
($filter->{UpdateDiskSpace}?'update disk space':()),
),
'returned' , scalar @Events , 'events',
@ -300,9 +302,9 @@ sub checkFilter {
Info("Archiving event $Event->{Id}");
# Do it individually to avoid locking up the table for new events
my $sql = 'UPDATE Events SET Archived = 1 WHERE Id = ?';
my $sth = $dbh->prepare_cached( $sql )
my $sth = $dbh->prepare_cached($sql)
or Fatal("Unable to prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute( $Event->{Id} )
my $res = $sth->execute($Event->{Id})
or Error("Unable to execute '$sql': ".$dbh->errstr());
}
if ( $Config{ZM_OPT_FFMPEG} && $filter->{AutoVideo} ) {
@ -343,6 +345,23 @@ sub checkFilter {
$_ = $Event->MoveTo($NewStorage);
Error($_) if $_;
}
if ( $filter->{AutoCopy} ) {
# Copy To is different from MoveTo in that it JUST copies the files
# So we still need to update the Event object with the new SecondaryStorageId
my $NewStorage = ZoneMinder::Storage->find_one(Id=>$filter->{AutoCopyTo});
if ( $NewStorage ) {
$_ = $Event->CopyTo($NewStorage);
if ( $_ ) {
$ZoneMinder::Database::dbh->commit();
Error($_);
} else {
$Event->save({SecondaryStorageId=>$$NewStorage{Id}});
$ZoneMinder::Database::dbh->commit();
}
} else {
Error("No storage area found for copy to operation. AutoCopyTo was $$filter{AutoCopyTo}");
}
} # end if AutoCopy
if ( $filter->{UpdateDiskSpace} ) {
$ZoneMinder::Database::dbh->begin_work();
@ -361,7 +380,7 @@ sub checkFilter {
$ZoneMinder::Database::dbh->commit();
} # end if UpdateDiskSpace
} # end foreach event
}
} # end sub checkFilter
sub generateVideo {
my $filter = shift;
@ -623,7 +642,7 @@ sub substituteTags {
my $Monitor = $Event->Monitor() if $need_monitor;
# Do we need the image information too?
my $need_images = $text =~ /%(?:EPI1|EPIM|EI1|EIM|EI1A|EIMA)%/;
my $need_images = $text =~ /%(?:EPI1|EPIM|EI1|EIM|EI1A|EIMA|EIMOD)%/;
my $first_alarm_frame;
my $max_alarm_frame;
my $max_alarm_score = 0;

View File

@ -88,13 +88,13 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
logInit();
logSetSignal();
Info( "Trigger daemon starting" );
Info('Trigger daemon starting');
my $dbh = zmDbConnect();
my $base_rin = '';
foreach my $connection ( @connections ) {
Info( "Opening connection '$connection->{name}'" );
Info("Opening connection '$connection->{name}'");
$connection->open();
}
@ -118,32 +118,32 @@ my $win = $rin;
my $ein = $win;
my $timeout = SELECT_TIMEOUT;
my %actions;
while( 1 ) {
while (1) {
$rin = $base_rin;
# Add the file descriptors of any spawned connections
foreach my $fileno ( keys(%spawned_connections) ) {
vec( $rin, $fileno, 1 ) = 1;
foreach my $fileno ( keys %spawned_connections ) {
vec($rin, $fileno, 1) = 1;
}
my $nfound = select( my $rout = $rin, undef, my $eout = $ein, $timeout );
my $nfound = select(my $rout = $rin, undef, my $eout = $ein, $timeout);
if ( $nfound > 0 ) {
Debug( "Got input from $nfound connections" );
Debug("Got input from $nfound connections");
foreach my $connection ( @in_select_connections ) {
if ( vec( $rout, $connection->fileno(), 1 ) ) {
Debug( 'Got input from connection '
if ( vec($rout, $connection->fileno(), 1) ) {
Debug('Got input from connection '
.$connection->name()
.' ('
.$connection->fileno()
.")"
.')'
);
if ( $connection->spawns() ) {
my $new_connection = $connection->accept();
$spawned_connections{$new_connection->fileno()} = $new_connection;
Debug( 'Added new spawned connection ('
Debug('Added new spawned connection ('
.$new_connection->fileno()
.'), '
.int(keys(%spawned_connections))
." spawned connections"
.' spawned connections'
);
} else {
my $messages = $connection->getMessages();
@ -152,30 +152,30 @@ while( 1 ) {
handleMessage( $connection, $message );
}
}
}
}
} # end if connection->spawns
} # end if vec
} # end foreach connection
foreach my $connection ( values(%spawned_connections) ) {
if ( vec( $rout, $connection->fileno(), 1 ) ) {
Debug( 'Got input from spawned connection '
if ( vec($rout, $connection->fileno(), 1) ) {
Debug('Got input from spawned connection '
.$connection->name()
.' ('
.$connection->fileno()
.")"
.')'
);
my $messages = $connection->getMessages();
if ( defined($messages) ) {
foreach my $message ( @$messages ) {
handleMessage( $connection, $message );
handleMessage($connection, $message);
}
} else {
delete( $spawned_connections{$connection->fileno()} );
Debug( 'Removed spawned connection ('
delete $spawned_connections{$connection->fileno()};
Debug('Removed spawned connection ('
.$connection->fileno()
.'), '
.int(keys(%spawned_connections))
." spawned connections"
.' spawned connections'
);
$connection->close();
}
@ -185,7 +185,7 @@ while( 1 ) {
if ( $! == EINTR ) {
# Do nothing
} else {
Fatal( "Can't select: $!" );
Fatal("Can't select: $!");
}
} # end if select returned activitiy
@ -194,14 +194,14 @@ while( 1 ) {
my $messages = $connection->getMessages();
if ( defined($messages) ) {
foreach my $message ( @$messages ) {
handleMessage( $connection, $message );
handleMessage($connection, $message);
}
}
}
# Check for alarms that might have happened
my @out_messages;
foreach my $monitor ( values(%monitors) ) {
foreach my $monitor ( values %monitors ) {
if ( ! zmMemVerify($monitor) ) {
# Our attempt to verify the memory handle failed. We should reload the monitors.
@ -225,7 +225,7 @@ while( 1 ) {
|| ($last_event != $monitor->{LastEvent})
) {
# A new event
push( @out_messages, $monitor->{Id}."|on|".time()."|".$last_event );
push @out_messages, $monitor->{Id}.'|on|'.time().'|'.$last_event;
} else {
# The same one as last time, so ignore it
# Do nothing
@ -236,42 +236,43 @@ while( 1 ) {
($state == STATE_TAPE && $monitor->{LastState} != STATE_TAPE)
) {
# Out of alarm state
push( @out_messages, $monitor->{Id}.'|off|'.time().'|'.$last_event );
push @out_messages, $monitor->{Id}.'|off|'.time().'|'.$last_event;
} elsif (
defined($monitor->{LastEvent})
&&
($last_event != $monitor->{LastEvent})
) {
# We've missed a whole event
push( @out_messages, $monitor->{Id}.'|on|'.time().'|'.$last_event );
push( @out_messages, $monitor->{Id}.'|off|'.time().'|'.$last_event );
push @out_messages, $monitor->{Id}.'|on|'.time().'|'.$last_event;
push @out_messages, $monitor->{Id}.'|off|'.time().'|'.$last_event;
}
$monitor->{LastState} = $state;
$monitor->{LastEvent} = $last_event;
} # end foreach monitor
foreach my $connection ( @out_connections ) {
if ( $connection->canWrite() ) {
$connection->putMessages( \@out_messages );
$connection->putMessages(\@out_messages);
}
}
foreach my $connection ( values(%spawned_connections) ) {
foreach my $connection ( values %spawned_connections ) {
if ( $connection->canWrite() ) {
$connection->putMessages( \@out_messages );
$connection->putMessages(\@out_messages);
}
}
if ( my @action_times = keys(%actions) ) {
Debug( "Checking for timed actions" );
Debug('Checking for timed actions');
my $now = time();
foreach my $action_time ( sort( grep { $_ < $now } @action_times ) ) {
Info( "Found actions expiring at $action_time" );
Info("Found " . scalar @{$actions{$action_time}} . "actions expiring at $action_time");
foreach my $action ( @{$actions{$action_time}} ) {
my $connection = $action->{connection};
my $message = $action->{message};
Info( "Found action '$message'" );
handleMessage( $connection, $message );
Info("Found action '$$action{message}'");
handleMessage($connection, $$action{message});
}
delete( $actions{$action_time} );
delete $actions{$action_time};
}
} # end if have timed actions
@ -280,15 +281,16 @@ while( 1 ) {
my $messages = $connection->timedActions();
if ( defined($messages) ) {
foreach my $message ( @$messages ) {
handleMessage( $connection, $message );
handleMessage($connection, $message);
}
}
}
foreach my $connection ( values(%spawned_connections) ) {
foreach my $connection ( values %spawned_connections ) {
my $messages = $connection->timedActions();
if ( defined($messages) ) {
foreach my $message ( @$messages ) {
handleMessage( $connection, $message );
handleMessage($connection, $message);
}
}
}
@ -317,14 +319,14 @@ exit;
sub loadMonitor {
my $monitor = shift;
Debug( "Loading monitor $monitor" );
zmMemInvalidate( $monitor );
Debug("Loading monitor $monitor");
zmMemInvalidate($monitor);
if ( zmMemVerify( $monitor ) ) { # This will re-init shared memory
$monitor->{LastState} = zmGetMonitorState( $monitor );
$monitor->{LastEvent} = zmGetLastEvent( $monitor );
if ( zmMemVerify($monitor) ) { # This will re-init shared memory
$monitor->{LastState} = zmGetMonitorState($monitor);
$monitor->{LastEvent} = zmGetLastEvent($monitor);
}
}
} # end sub loadMonitor
sub loadMonitors {
Debug('Loading monitors');
@ -332,18 +334,19 @@ sub loadMonitors {
my %new_monitors = ();
my $sql = "SELECT * FROM Monitors
WHERE find_in_set( Function, 'Modect,Mocord,Nodect' )".
( $Config{ZM_SERVER_ID} ? 'AND ServerId=?' : '' )
my $sql = q`SELECT * FROM Monitors
WHERE find_in_set( Function, 'Modect,Mocord,Nodect' )`.
( $Config{ZM_SERVER_ID} ? ' AND ServerId=?' : '' )
;
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : () )
or Fatal( "Can't execute: ".$sth->errstr() );
while( my $monitor = $sth->fetchrow_hashref() ) {
if ( zmMemVerify( $monitor ) ) { # This will re-init shared memory
$monitor->{LastState} = zmGetMonitorState( $monitor );
$monitor->{LastEvent} = zmGetLastEvent( $monitor );
while ( my $monitor = $sth->fetchrow_hashref() ) {
if ( zmMemVerify($monitor) ) { # This will re-init shared memory
$monitor->{LastState} = zmGetMonitorState($monitor);
$monitor->{LastEvent} = zmGetLastEvent($monitor);
}
$new_monitors{$monitor->{Id}} = $monitor;
} # end while fetchrow
@ -367,7 +370,7 @@ sub handleMessage {
}
Debug("Found monitor for id '$id'");
next if ( !zmMemVerify($monitor) );
next if !zmMemVerify($monitor);
Debug("Handling action '$action'");
if ( $action =~ /^(enable|disable)(?:\+(\d+))?$/ ) {
@ -412,20 +415,20 @@ sub handleMessage {
zmTriggerShowtext($monitor, $showtext) if defined($showtext);
Info("Trigger '$trigger'");
# Wait til it's finished
while( zmInAlarm($monitor)
while ( zmInAlarm($monitor)
&& ($last_event == zmGetLastEvent($monitor))
) {
# Tenth of a second
usleep(100000);
}
zmTriggerEventCancel($monitor);
}
} # end if delay or not
} # end if trigger is on or off
} elsif( $action eq 'cancel' ) {
} elsif ( $action eq 'cancel' ) {
zmTriggerEventCancel($monitor);
zmTriggerShowtext($monitor, $showtext) if defined($showtext);
Info('Cancelled event');
} elsif( $action eq 'show' ) {
} elsif ( $action eq 'show' ) {
zmTriggerShowtext( $monitor, $showtext );
Info("Updated show text to '$showtext'");
} else {
@ -439,11 +442,26 @@ sub handleDelay {
my $action_text = shift;
my $action_time = time()+$delay;
# Need to check and cancel previous actions. See issue #2619
foreach my $a_time ( keys %actions ) {
if ( $a_time <= $action_time ) {
for ( my $i = 0; $i < @{$actions{$a_time}}; $i ++ ) {
my $action = $actions{$a_time}[$i];
if ( $$action{message} eq $action_text ) {
Info("Found duplicate action '$$action{message}' at $a_time, cancelling it");
splice @{$actions{$a_time}}, $i, 1;
}
} # end foreach action
delete $actions{$a_time} if !@{$actions{$a_time}};
} # end if
} # end foreach action_time
my $action_array = $actions{$action_time};
if ( !$action_array ) {
$action_array = $actions{$action_time} = [];
}
push( @$action_array, { connection=>$connection, message=>$action_text } );
push @$action_array, { connection=>$connection, message=>$action_text };
Debug("Added timed event '$action_text', expires at $action_time (+$delay secs)");
}

View File

@ -45,7 +45,7 @@ protected:
unsigned int colours;
unsigned int subpixelorder;
unsigned int pixels;
unsigned int imagesize;
unsigned long long imagesize;
int brightness;
int hue;
int colour;
@ -73,7 +73,7 @@ public:
unsigned int Colours() const { return colours; }
unsigned int SubpixelOrder() const { return subpixelorder; }
unsigned int Pixels() const { return pixels; }
unsigned int ImageSize() const { return imagesize; }
unsigned long long ImageSize() const { return imagesize; }
unsigned int Bytes() const { return bytes; };
virtual int Brightness( int/*p_brightness*/=-1 ) { return -1; }

View File

@ -287,18 +287,6 @@ static void zm_log_fps(double d, const char *postfix) {
}
}
void zm_dump_video_frame(const AVFrame *frame, const char *text) {
Debug(1, "%s: format %d %s %dx%d linesize:%d pts: %" PRId64,
text,
frame->format,
av_get_pix_fmt_name((AVPixelFormat)frame->format),
frame->width,
frame->height,
frame->linesize,
frame->pts
);
}
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
void zm_dump_codecpar ( const AVCodecParameters *par ) {
Debug(1, "Dumping codecpar codec_type(%d) codec_id(%d) codec_tag(%d) width(%d) height(%d) bit_rate(%d) format(%d = %s)",

View File

@ -329,7 +329,15 @@ void zm_dump_codecpar(const AVCodecParameters *par);
#endif
void zm_dump_video_frame(const AVFrame *frame, const char *text="Frame");
#define zm_dump_video_frame(frame,text) Debug(1, "%s: format %d %s %dx%d linesize:%dx%d pts: %" PRId64, \
text, \
frame->format, \
av_get_pix_fmt_name((AVPixelFormat)frame->format), \
frame->width, \
frame->height, \
frame->linesize[0], frame->linesize[1], \
frame->pts \
);
#if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100)
#define zm_av_packet_unref( packet ) av_packet_unref( packet )

View File

@ -44,6 +44,7 @@ extern "C" {
#if HAVE_LIBAVUTIL_HWCONTEXT_H
#if LIBAVCODEC_VERSION_CHECK(57, 89, 0, 89, 0)
static enum AVPixelFormat hw_pix_fmt;
static enum AVPixelFormat get_hw_format(
AVCodecContext *ctx,
@ -94,6 +95,7 @@ static enum AVPixelFormat find_fmt_by_hw_type(const enum AVHWDeviceType type) {
}
#endif
#endif
#endif
FfmpegCamera::FfmpegCamera(
int p_id,
@ -155,8 +157,10 @@ FfmpegCamera::FfmpegCamera(
#if HAVE_LIBAVUTIL_HWCONTEXT_H
hwFrame = NULL;
hw_device_ctx = NULL;
#if LIBAVCODEC_VERSION_CHECK(57, 89, 0, 89, 0)
hw_pix_fmt = AV_PIX_FMT_NONE;
#endif
#endif
#if HAVE_LIBSWSCALE
mConvertContext = NULL;
@ -437,6 +441,8 @@ int FfmpegCamera::OpenFfmpeg() {
if ( hwaccel_name != "" ) {
#if HAVE_LIBAVUTIL_HWCONTEXT_H
// 3.2 doesn't seem to have all the bits in place, so let's require 3.3 and up
#if LIBAVCODEC_VERSION_CHECK(57, 89, 0, 89, 0)
// Print out available types
enum AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE;
while ( (type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE )
@ -497,6 +503,9 @@ int FfmpegCamera::OpenFfmpeg() {
} else {
Debug(1, "Failed to setup hwaccel.");
}
#else
Debug(1, "AVCodec not new enough for hwaccel");
#endif
#else
Warning("HWAccel support not compiled in.");
#endif
@ -944,8 +953,10 @@ int FfmpegCamera::CaptureAndRecord(
continue;
}
if ( error_count > 0 ) error_count--;
zm_dump_video_frame(mRawFrame);
Debug(3, "Decoded video packet at frame %d", frameCount);
zm_dump_video_frame(mRawFrame, "raw frame from decoder");
#if HAVE_LIBAVUTIL_HWCONTEXT_H
#if LIBAVCODEC_VERSION_CHECK(57, 89, 0, 89, 0)
if (
(hw_pix_fmt != AV_PIX_FMT_NONE)
&&
@ -964,14 +975,18 @@ int FfmpegCamera::CaptureAndRecord(
hwFrame->pts = mRawFrame->pts;
input_frame = hwFrame;
} else {
#endif
#endif
input_frame = mRawFrame;
#if HAVE_LIBAVUTIL_HWCONTEXT_H
#if LIBAVCODEC_VERSION_CHECK(57, 89, 0, 89, 0)
}
#endif
#endif
Debug(4, "Got frame %d", frameCount);
if ( transfer_to_image(image, mFrame, input_frame) < 0 ) {
Error("Failed to transfer from frame to image");
zm_av_packet_unref(&packet);
return -1;
}
@ -1038,8 +1053,13 @@ int FfmpegCamera::transfer_to_image(
return -1;
}
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
av_image_fill_arrays(output_frame->data, output_frame->linesize,
directbuffer, imagePixFormat, width, height, 1);
int size = av_image_fill_arrays(
output_frame->data, output_frame->linesize,
directbuffer, imagePixFormat, width, height, 32);
if ( size < 0 ) {
Error("Problem setting up data pointers into image %s",
av_make_error_string(size).c_str());
}
#else
avpicture_fill((AVPicture *)output_frame, directbuffer,
imagePixFormat, width, height);
@ -1061,6 +1081,12 @@ int FfmpegCamera::transfer_to_image(
);
return -1;
}
Debug(1, "Setup conversion context for %dx%d %s to %dx%d %s",
input_frame->width, input_frame->height,
av_get_pix_fmt_name((AVPixelFormat)input_frame->format),
width, height,
av_get_pix_fmt_name(imagePixFormat)
);
}
if ( sws_scale(

View File

@ -497,8 +497,8 @@ uint8_t* Image::WriteBuffer(const unsigned int p_width, const unsigned int p_hei
return NULL;
}
if ( !p_height || !p_width ) {
Error("WriteBuffer called with invalid width or height: %d %d",p_width,p_height);
if ( ! ( p_height > 0 && p_width > 0 ) ) {
Error("WriteBuffer called with invalid width or height: %d %d", p_width, p_height);
return NULL;
}
@ -525,11 +525,10 @@ uint8_t* Image::WriteBuffer(const unsigned int p_width, const unsigned int p_hei
colours = p_colours;
subpixelorder = p_subpixelorder;
pixels = height*width;
size = newsize;
}
size = newsize;
} // end if need to re-alloc buffer
return buffer;
}
/* Assign an existing buffer to the image instead of copying from a source buffer. The goal is to reduce the amount of memory copying and increase efficiency and buffer reusing. */

View File

@ -56,9 +56,9 @@ extern imgbufcpy_fptr_t fptr_imgbufcpy;
/* Should be called from Image class functions */
inline static uint8_t* AllocBuffer(size_t p_bufsize) {
uint8_t* buffer = (uint8_t*)zm_mallocaligned(64,p_bufsize);
uint8_t* buffer = (uint8_t*)zm_mallocaligned(64, p_bufsize);
if ( buffer == NULL )
Fatal("Memory allocation failed: %s",strerror(errno));
Fatal("Memory allocation failed: %s", strerror(errno));
return buffer;
}
@ -75,7 +75,7 @@ inline static void DumpBuffer(uint8_t* buffer, int buffertype) {
av_free(buffer);
*/
} else {
Error( "Unknown buffer type in DumpBuffer(%d)", buffertype );
Error("Unknown buffer type in DumpBuffer(%d)", buffertype);
}
}
}

View File

@ -422,8 +422,12 @@ Monitor::Monitor(
+ (image_buffer_count*camera->ImageSize())
+ 64; /* Padding used to permit aligning the images buffer to 64 byte boundary */
Debug(1, "mem.size SharedData=%d TriggerData=%d VideoStoreData=%d total=%" PRId64,
sizeof(SharedData), sizeof(TriggerData), sizeof(VideoStoreData), mem_size);
Debug(1, "mem.size(%d) SharedData=%d TriggerData=%d VideoStoreData=%d timestamps=%d images=%dx%d = %" PRId64 " total=%" PRId64,
sizeof(mem_size),
sizeof(SharedData), sizeof(TriggerData), sizeof(VideoStoreData),
(image_buffer_count*sizeof(struct timeval)),
image_buffer_count, camera->ImageSize(), (image_buffer_count*camera->ImageSize()),
mem_size);
mem_ptr = NULL;
storage = new Storage(storage_id);
@ -599,7 +603,7 @@ bool Monitor::connect() {
if ( shm_id < 0 ) {
Fatal("Can't shmget, probably not enough shared memory space free: %s", strerror(errno));
}
mem_ptr = (unsigned char *)shmat( shm_id, 0, 0 );
mem_ptr = (unsigned char *)shmat(shm_id, 0, 0);
if ( mem_ptr < (void *)0 ) {
Fatal("Can't shmat: %s", strerror(errno));
}

View File

@ -1010,7 +1010,7 @@ int VideoStore::writeVideoFramePacket(AVPacket *ipkt) {
# if 1
if ( opkt.dts < video_out_stream->cur_dts ) {
Warning("Fixing non-monotonic dts/pts dts %" PRId64 " pts %" PRId64 " stream %" PRId64,
Debug(1, "Fixing non-monotonic dts/pts dts %" PRId64 " pts %" PRId64 " stream %" PRId64,
opkt.dts, opkt.pts, video_out_stream->cur_dts);
opkt.dts = video_out_stream->cur_dts;
if ( opkt.dts > opkt.pts ) {

View File

@ -1 +1 @@
1.33.13
1.33.14

View File

@ -12,6 +12,7 @@ class Event {
'Name',
'MonitorId',
'StorageId',
'SecondaryStorageId',
'Name',
'Cause',
'StartTime',
@ -85,6 +86,19 @@ class Event {
return $this->{'Storage'};
}
public function SecondaryStorage( $new = null ) {
if ( $new ) {
$this->{'SecondaryStorage'} = $new;
}
if ( ! ( array_key_exists('SecondaryStorage', $this) and $this->{'SecondaryStorage'} ) ) {
if ( isset($this->{'SecondaryStorageId'}) and $this->{'SecondaryStorageId'} )
$this->{'SecondaryStorage'} = Storage::find_one(array('Id'=>$this->{'SecondaryStorageId'}));
if ( ! ( array_key_exists('SecondaryStorage', $this) and $this->{'SecondaryStorage'} ) )
$this->{'SecondaryStorage'} = new Storage(NULL);
}
return $this->{'SecondaryStorage'};
}
public function Monitor() {
if ( isset($this->{'MonitorId'}) ) {
$Monitor = Monitor::find_one(array('Id'=>$this->{'MonitorId'}));

View File

@ -1,9 +1,11 @@
<?php
namespace ZM;
require_once('Object.php');
class Filter {
class Filter extends ZM_Object {
protected static $table = 'Filters';
public $defaults = array(
protected $defaults = array(
'Id' => null,
'Name' => '',
'AutoExecute' => 0,
@ -16,68 +18,55 @@ public $defaults = array(
'AutoMessage' => 0,
'AutoMove' => 0,
'AutoMoveTo' => 0,
'AutoCopy' => 0,
'AutoCopyTo' => 0,
'UpdateDiskSpace' => 0,
'Background' => 0,
'Concurrent' => 0,
'limit' => 100,
'Query' => array(),
'sort_field' => ZM_WEB_EVENT_SORT_FIELD,
'sort_asc' => ZM_WEB_EVENT_SORT_ORDER,
);
'Query_json' => '',
);
public function __construct( $IdOrRow=NULL ) {
$row = NULL;
if ( $IdOrRow ) {
if ( is_integer($IdOrRow) or is_numeric($IdOrRow) ) {
$row = dbFetchOne('SELECT * FROM Filters WHERE Id=?', NULL, array($IdOrRow));
if ( ! $row ) {
Error('Unable to load Filter record for Id=' . $IdOrRow);
}
} elseif ( is_array($IdOrRow) ) {
$row = $IdOrRow;
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Error("Unknown argument passed to Filter Constructor from $file:$line)");
Error("Unknown argument passed to Filter Constructor ($IdOrRow)");
return;
}
} # end if isset($IdOrRow)
public function Query_json() {
if ( func_num_args( ) ) {
$this->{'Query_json'} = func_get_arg(0);;
$this->{'Query'} = jsonDecode($this->{'Query_json'});
}
return $this->{'Query_json'};
}
if ( $row ) {
foreach ($row as $k => $v) {
$this->{$k} = $v;
}
if ( array_key_exists('Query', $this) and $this->{'Query'} ) {
$this->{'Query'} = jsonDecode($this->{'Query'});
public function Query() {
if ( func_num_args( ) ) {
$this->{'Query'} = func_get_arg(0);;
$this->{'Query_json'} = jsonEncode($this->{'Query'});
}
if ( !array_key_exists('Query', $this) ) {
if ( array_key_exists('Query_json', $this) and $this->{'Query_json'} ) {
$this->{'Query'} = jsonDecode($this->{'Query_json'});
} else {
$this->{'Query'} = array();
}
}
} // end function __construct
public function __call( $fn, array $args ) {
if ( count( $args ) ) {
$this->{$fn} = $args[0];
}
if ( array_key_exists( $fn, $this ) ) {
return $this->{$fn};
} else if ( array_key_exists( $fn, $this->defaults ) ) {
$this->{$fn} = $this->defaults{$fn};
return $this->{$fn};
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Warning( "Unknown function call Filter->$fn from $file:$line" );
if ( !is_array($this->{'Query'}) ) {
# Handle existence of both Query_json and Query in the row
$this->{'Query'} = jsonDecode($this->{'Query_json'});
}
}
return $this->{'Query'};
}
public static function find( $parameters = array(), $options = array() ) {
return ZM_Object::_find(get_class(), $parameters, $options);
}
public static function find_one( $parameters = array(), $options = array() ) {
return ZM_Object::_find_one(get_class(), $parameters, $options);
}
public function terms( ) {
if ( func_num_args( ) ) {
$this->Query()['terms'] = func_get_arg(0);
if ( func_num_args() ) {
$Query = $this->Query();
$Query['terms'] = func_get_arg(0);
$this->Query($Query);
}
if ( isset( $this->Query()['terms'] ) ) {
return $this->Query()['terms'];
@ -88,108 +77,44 @@ public $defaults = array(
// The following three fields are actually stored in the Query
public function sort_field( ) {
if ( func_num_args( ) ) {
$this->Query()['sort_field'] = func_get_arg(0);
$Query = $this->Query();
$Query['sort_field'] = func_get_arg(0);
$this->Query($Query);
}
if ( isset( $this->Query()['sort_field'] ) ) {
return $this->{'Query'}['sort_field'];
}
return $this->defaults{'sort_field'};
return ZM_WEB_EVENT_SORT_FIELD;
#return $this->defaults{'sort_field'};
}
public function sort_asc( ) {
if ( func_num_args( ) ) {
$this->{'Query'}['sort_asc'] = func_get_arg(0);
$Query = $this->Query();
$Query['sort_asc'] = func_get_arg(0);
$this->Query($Query);
}
if ( isset( $this->Query()['sort_asc'] ) ) {
return $this->{'Query'}['sort_asc'];
}
return $this->defaults{'sort_asc'};
return ZM_WEB_EVENT_SORT_ORDER;
#return $this->defaults{'sort_asc'};
}
public function limit( ) {
if ( func_num_args( ) ) {
$this->{'Query'}['limit'] = func_get_arg(0);
$Query = $this->Query();
$Query['limit'] = func_get_arg(0);
$this->Query($Query);
}
if ( isset( $this->Query()['limit'] ) )
return $this->{'Query'}['limit'];
return $this->defaults{'limit'};
return 100;
#return $this->defaults{'limit'};
}
public static function find( $parameters = null, $options = null ) {
$filters = array();
$sql = 'SELECT * FROM Filters ';
$values = array();
if ( $parameters ) {
$fields = array();
$sql .= 'WHERE ';
foreach ( $parameters as $field => $value ) {
if ( $value == null ) {
$fields[] = $field.' IS NULL';
} else if ( is_array( $value ) ) {
$func = function(){return '?';};
$fields[] = $field.' IN ('.implode(',', array_map($func, $value)). ')';
$values += $value;
} else {
$fields[] = $field.'=?';
$values[] = $value;
}
}
$sql .= implode(' AND ', $fields);
}
if ( $options ) {
if ( isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
}
if ( isset($options['limit']) ) {
if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) {
$sql .= ' LIMIT ' . $options['limit'];
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Error("Invalid value for limit(".$options['limit'].") passed to Filter::find from $file:$line");
return array();
}
}
}
$result = dbQuery($sql, $values);
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Filter');
foreach ( $results as $row => $obj ) {
$filters[] = $obj;
}
return $filters;
} # end find()
public static function find_one( $parameters = array() ) {
$results = Filter::find($parameters, array('limit'=>1));
if ( ! sizeof($results) ) {
return;
}
return $results[0];
} # end find_one()
public function delete() {
dbQuery('DELETE FROM Filters WHERE Id=?', array($this->{'Id'}));
} # end function delete()
public function set( $data ) {
foreach ($data as $k => $v) {
if ( is_array( $v ) ) {
$this->{$k} = $v;
} else if ( is_string( $v ) ) {
$this->{$k} = trim( $v );
} else if ( is_integer( $v ) ) {
$this->{$k} = $v;
} else if ( is_bool( $v ) ) {
$this->{$k} = $v;
} else {
Error( "Unknown type $k => $v of var " . gettype( $v ) );
$this->{$k} = $v;
}
}
} # end function set
public function control($command, $server_id=null) {
$Servers = $server_id ? Server::find(array('Id'=>$server_id)) : Server::find();
$Servers = $server_id ? Server::find(array('Id'=>$server_id)) : Server::find(array('Status'=>'Running'));
if ( !count($Servers) and !$server_id ) {
# This will be the non-multi-server case
$Servers = array(new Server());
@ -199,7 +124,7 @@ public $defaults = array(
if ( !defined('ZM_SERVER_ID') or !$Server->Id() or ZM_SERVER_ID==$Server->Id() ) {
# Local
Logger::Debug("Controlling filter locally $command for server ".$Server->Id());
daemonControl($command, 'zmfilter.pl', '--filter_id='.$this->{'Id'});
daemonControl($command, 'zmfilter.pl', '--filter_id='.$this->{'Id'}.' --daemon');
} else {
# Remote case

View File

@ -1,127 +1,21 @@
<?php
namespace ZM;
$group_cache = array();
class Group {
public $defaults = array(
class Group extends ZM_Object {
protected static $table = 'Groups';
protected $defaults = array(
'Id' => null,
'Name' => '',
'ParentId' => null,
);
public function __construct( $IdOrRow=NULL ) {
global $group_cache;
$row = NULL;
if ( $IdOrRow ) {
if ( is_integer($IdOrRow) or is_numeric($IdOrRow) ) {
$row = dbFetchOne('SELECT * FROM Groups WHERE Id=?', NULL, array($IdOrRow));
if ( ! $row ) {
Error('Unable to load Group record for Id=' . $IdOrRow);
}
} elseif ( is_array($IdOrRow) ) {
$row = $IdOrRow;
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Error("Unknown argument passed to Group Constructor from $file:$line)");
Error("Unknown argument passed to Group Constructor ($IdOrRow)");
return;
}
} # end if isset($IdOrRow)
if ( $row ) {
foreach ($row as $k => $v) {
$this->{$k} = $v;
}
$group_cache[$row['Id']] = $this;
}
} // end function __construct
public function __call($fn, array $args) {
if ( count($args) ) {
$this->{$fn} = $args[0];
}
if ( array_key_exists($fn, $this) ) {
return $this->{$fn};
} else if ( array_key_exists( $fn, $this->defaults ) ) {
$this->{$fn} = $this->defaults{$fn};
return $this->{$fn};
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Warning( "Unknown function call Group->$fn from $file:$line" );
}
public static function find( $parameters = array(), $options = array() ) {
return ZM_Object::_find(get_class(), $parameters, $options);
}
public static function find( $parameters = null, $options = null ) {
$sql = 'SELECT * FROM Groups ';
$values = array();
if ( $parameters ) {
$fields = array();
$sql .= 'WHERE ';
foreach ( $parameters as $field => $value ) {
if ( $value == null ) {
$fields[] = $field.' IS NULL';
} else if ( is_array( $value ) ) {
$func = function(){return '?';};
$fields[] = $field.' IN ('.implode(',', array_map($func, $value)). ')';
$values += $value;
} else {
$fields[] = $field.'=?';
$values[] = $value;
}
}
$sql .= implode(' AND ', $fields);
} # end if parameters
if ( $options ) {
if ( isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
}
if ( isset($options['limit']) ) {
if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) {
$sql .= ' LIMIT ' . $options['limit'];
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Error("Invalid value for limit(".$options['limit'].") passed to Group::find from $file:$line");
return array();
}
}
} # end if options
$results = dbFetchAll($sql, NULL, $values);
if ( $results ) {
return array_map( function($row){ return new Group($row); }, $results );
}
return array();
} # end find()
public static function find_one($parameters = null, $options = null) {
global $group_cache;
if (
( count($parameters) == 1 ) and
isset($parameters['Id']) and
isset($group_cache[$parameters['Id']]) ) {
return $group_cache[$parameters['Id']];
}
$results = Group::find($parameters, $options);
if ( count($results) > 1 ) {
Error("Group::find_one Returned more than 1");
return $results[0];
} else if ( count($results) ) {
return $results[0];
} else {
return null;
}
} # end function find_one
public static function find_one( $parameters = array(), $options = array() ) {
return ZM_Object::_find_one(get_class(), $parameters, $options);
}
public function delete() {
if ( array_key_exists('Id', $this) ) {
@ -137,23 +31,6 @@ class Group {
}
} # end function delete()
public function set( $data ) {
foreach ($data as $k => $v) {
if ( is_array($v) ) {
$this->{$k} = $v;
} else if ( is_string($v) ) {
$this->{$k} = trim( $v );
} else if ( is_integer($v) ) {
$this->{$k} = $v;
} else if ( is_bool($v) ) {
$this->{$k} = $v;
} else {
Error("Unknown type $k => $v of var " . gettype($v));
$this->{$k} = $v;
}
}
} # end function set
public function depth( $new = null ) {
if ( isset($new) ) {
$this->{'depth'} = $new;

254
web/includes/Object.php Normal file
View File

@ -0,0 +1,254 @@
<?php
namespace ZM;
require_once('database.php');
$object_cache = array();
class ZM_Object {
public function __construct($IdOrRow = NULL) {
$class = get_class($this);
global $object_cache;
if ( ! isset($object_cache[$class]) )
$object_cache[$class] = array();
$cache = $object_cache[$class];
$table = $class::$table;
$row = NULL;
if ( $IdOrRow ) {
if ( is_integer($IdOrRow) or ctype_digit($IdOrRow) ) {
$row = dbFetchOne("SELECT * FROM `$table` WHERE `Id`=?", NULL, array($IdOrRow));
if ( !$row ) {
Error("Unable to load $class record for Id=$IdOrRow");
}
} elseif ( is_array($IdOrRow) ) {
$row = $IdOrRow;
}
} # end if isset($IdOrRow)
if ( $row ) {
foreach ($row as $k => $v) {
$this->{$k} = $v;
}
$cache[$row['Id']] = $this;
} else {
# Set defaults
foreach ( $this->defaults as $k => $v ) $this->{$k} = $v;
}
}
public function __call($fn, array $args){
if ( count($args) ) {
$this->{$fn} = $args[0];
}
if ( array_key_exists($fn, $this) ) {
return $this->{$fn};
} else {
if ( array_key_exists($fn, $this->defaults) ) {
return $this->defaults{$fn};
} else {
$backTrace = debug_backtrace();
Warning("Unknown function call Sensor->$fn from ".print_r($backTrace,true));
}
}
}
public static function _find($class, $parameters = null, $options = null ) {
$table = $class::$table;
$filters = array();
$sql = "SELECT * FROM `$table` ";
$values = array();
if ( $parameters ) {
$fields = array();
$sql .= 'WHERE ';
foreach ( $parameters as $field => $value ) {
if ( $value == null ) {
$fields[] = '`'.$field.'` IS NULL';
} else if ( is_array($value) ) {
$func = function(){return '?';};
$fields[] = '`'.$field.'` IN ('.implode(',', array_map($func, $value)). ')';
$values += $value;
} else {
$fields[] = '`'.$field.'`=?';
$values[] = $value;
}
}
$sql .= implode(' AND ', $fields );
}
if ( $options ) {
if ( isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
}
if ( isset($options['limit']) ) {
if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) {
$sql .= ' LIMIT ' . $options['limit'];
} else {
Error('Invalid value for limit('.$options['limit'].') passed to '.get_class()."::find from ".print_r($backTrace,true));
return array();
}
}
}
$rows = dbFetchAll($sql, NULL, $values);
$results = array();
if ( $rows ) {
foreach ( $rows as $row ) {
array_push($results , new $class($row));
}
}
return $results;
} # end public function find()
public static function _find_one($class, $parameters = array(), $options = array() ) {
global $object_cache;
if ( ! isset($object_cache[$class]) )
$object_cache[$class] = array();
$cache = $object_cache[$class];
if (
( count($parameters) == 1 ) and
isset($parameters['Id']) and
isset($cache[$parameters['Id']]) ) {
return $cache[$parameters['Id']];
}
$options['limit'] = 1;
$results = ZM_Object::_find($class, $parameters, $options);
if ( ! sizeof($results) ) {
return;
}
return $results[0];
}
public static function Objects_Indexed_By_Id($class) {
$results = array();
foreach ( ZM_Object::_find($class, null, array('order'=>'lower(Name)')) as $Object ) {
$results[$Object->Id()] = $Object;
}
return $results;
}
public function to_json() {
$json = array();
foreach ($this->defaults as $key => $value) {
if ( is_callable(array($this, $key)) ) {
$json[$key] = $this->$key();
} else if ( array_key_exists($key, $this) ) {
$json[$key] = $this->{$key};
} else {
$json[$key] = $this->defaults{$key};
}
}
return json_encode($json);
}
public function set($data) {
foreach ( $data as $k => $v ) {
if ( method_exists($this, $k) ) {
$this->{$k}($v);
} else {
if ( is_array($v) ) {
# perhaps should turn into a comma-separated string
$this->{$k} = implode(',', $v);
} else if ( is_string($v) ) {
if ( $v == '' and array_key_exists($k, $this->defaults) ) {
$this->{$k} = $this->defaults[$k];
} else {
$this->{$k} = trim($v);
}
} else if ( is_integer($v) ) {
$this->{$k} = $v;
} else if ( is_bool($v) ) {
$this->{$k} = $v;
} else if ( is_null($v) ) {
$this->{$k} = $v;
} else {
Error( "Unknown type $k => $v of var " . gettype( $v ) );
$this->{$k} = $v;
}
} # end if method_exists
} # end foreach $data as $k=>$v
}
public function changes( $new_values ) {
$changes = array();
foreach ( $new_values as $field => $value ) {
if ( method_exists($this, $field) ) {
$old_value = $this->$field();
Logger::Debug("Checking method $field () ".print_r($old_value,true)." => " . print_r($value,true));
if ( is_array($old_value) ) {
$diff = array_recursive_diff($old_value, $value);
Logger::Debug("Checking method $field () diff is".print_r($diff,true));
if ( count($diff) ) {
$changes[$field] = $value;
}
} else if ( $this->$field() != $value ) {
$changes[$field] = $value;
}
} else if ( array_key_exists($field, $this) ) {
Logger::Debug("Checking field $field => ".$this->{$field} . " ?= " .$value);
if ( $this->{$field} != $value ) {
$changes[$field] = $value;
}
} else if ( array_key_exists($field, $this->defaults) ) {
Logger::Debug("Checking default $field => ".$this->defaults[$field] . " " .$value);
if ( $this->defaults[$field] != $value ) {
$changes[$field] = $value;
}
}
#if ( (!array_key_exists($field, $this)) or ( $this->{$field} != $new_values[$field] ) ) {
#Logger::Debug("Checking default $field => $default_value changes becaause" . $new_values[$field].' != '.$new_values[$field]);
#$changes[$field] = $new_values[$field];
##} else if {
#Logger::Debug("Checking default $field => $default_value changes becaause " . $new_values[$field].' != '.$new_values[$field]);
##array_push( $changes, [$field=>$defaults[$field]] );
#}
#} else {
#Logger::Debug("Checking default $field => $default_value not in new_values");
#}
} # end foreach default
return $changes;
} # end public function changes
public function save($new_values = null) {
$class = get_class($this);
$table = $class::$table;
if ( $new_values ) {
Logger::Debug("New values" . print_r($new_values,true));
$this->set($new_values);
}
if ( $this->Id() ) {
$fields = array_keys($this->defaults);
$sql = 'UPDATE '.$table.' SET '.implode(', ', array_map(function($field) {return '`'.$field.'`=?';}, $fields )) . ' WHERE Id=?';
$values = array_map(function($field){return $this->{$field};}, $fields);
$values[] = $this->{'Id'};
if ( dbQuery($sql, $values) )
return true;
} else {
$fields = $this->defaults;
unset($fields['Id']);
$sql = 'INSERT INTO '.$table.' ('.implode(', ', array_map(function($field) {return '`'.$field.'`';}, array_keys($fields))).') VALUES ('.implode(', ', array_map(function($field){return '?';}, array_values($fields))).')';
$values = array_map(function($field){return $this->{$field};}, array_keys($fields));
if ( dbQuery($sql, $values) ) {
$this->{'Id'} = dbInsertId();
return true;
}
}
return false;
} // end function save
public function delete() {
$class = get_class($this);
$table = $class::$table;
dbQuery("DELETE FROM $table WHERE Id=?", array($this->{'Id'}));
if ( isset($object_cache[$class]) and isset($object_cache[$class][$this->{'Id'}]) )
unset($object_cache[$class][$this->{'Id'}]);
}
} # end class Sensor Action
?>

View File

@ -2,10 +2,11 @@
namespace ZM;
require_once('database.php');
require_once('Event.php');
require_once('Object.php');
$storage_cache = array();
class Storage {
private $defaults = array(
class Storage extends ZM_Object {
protected static $table = 'Storage';
protected $defaults = array(
'Id' => null,
'Path' => '',
'Name' => '',
@ -16,31 +17,12 @@ class Storage {
'ServerId' => 0,
'DoDelete' => 1,
);
public static function find($parameters = array(), $options = array() ) {
return ZM_Object::_find(get_class(), $parameters, $options);
}
public function __construct( $IdOrRow = NULL ) {
global $storage_cache;
$row = NULL;
if ( $IdOrRow ) {
if ( is_integer($IdOrRow) or is_numeric($IdOrRow) ) {
$row = dbFetchOne('SELECT * FROM Storage WHERE Id=?', NULL, array($IdOrRow));
if ( ! $row ) {
Error('Unable to load Storage record for Id=' . $IdOrRow);
}
} else if ( is_array($IdOrRow) ) {
$row = $IdOrRow;
}
}
if ( $row ) {
foreach ($row as $k => $v) {
$this->{$k} = $v;
}
$storage_cache[$row['Id']] = $this;
} else {
$this->{'Name'} = '';
$this->{'Path'} = '';
$this->{'Type'} = 'local';
}
public static function find_one( $parameters = array(), $options = array() ) {
return ZM_Object::_find_one(get_class(), $parameters, $options);
}
public function Path() {
@ -66,93 +48,6 @@ class Storage {
return $this->{'Name'};
}
public function __call( $fn, array $args= NULL ) {
if ( count($args) ) {
$this->{$fn} = $args[0];
}
if ( array_key_exists($fn, $this) )
return $this->{$fn};
if ( array_key_exists($fn, $this->defaults) )
return $this->defaults{$fn};
$backTrace = debug_backtrace();
$file = $backTrace[0]['file'];
$line = $backTrace[0]['line'];
Warning("Unknown function call Storage->$fn from $file:$line");
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Warning("Unknown function call Storage->$fn from $file:$line");
}
public static function find_one( $parameters = null, $options = null ) {
global $storage_cache;
if (
( count($parameters) == 1 ) and
isset($parameters['Id']) and
isset($storage_cache[$parameters['Id']]) ) {
return $storage_cache[$parameters['Id']];
}
$results = Storage::find($parameters, $options);
if ( count($results) > 1 ) {
Error('Storage Returned more than 1');
return $results[0];
} else if ( count($results) ) {
return $results[0];
} else {
return null;
}
}
public static function find( $parameters = null, $options = null ) {
$sql = 'SELECT * FROM Storage ';
$values = array();
if ( $parameters ) {
$fields = array();
$sql .= 'WHERE ';
foreach ( $parameters as $field => $value ) {
if ( $value == null ) {
$fields[] = $field.' IS NULL';
} else if ( is_array($value) ) {
$func = function(){return '?';};
$fields[] = $field.' IN ('.implode(',', array_map($func, $value)).')';
$values += $value;
} else {
$fields[] = $field.'=?';
$values[] = $value;
}
}
$sql .= implode(' AND ', $fields);
} # end if parameters
if ( $options ) {
if ( isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
} # end if options
if ( isset($options['limit']) ) {
if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) {
$sql .= ' LIMIT ' . $option['limit'];
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Error("Invalid value for limit(".$options['limit'].") passed to Control::find from $file:$line");
return array();
}
} # end if limit
} # end if options
$storage_areas = array();
$result = dbQuery($sql, $values);
if ( $result ) {
$results = $result->fetchALL();
foreach ( $results as $row ) {
$storage_areas[] = new Storage($row);
}
}
return $storage_areas;
} # end find()
public function disk_usage_percent() {
$path = $this->Path();
if ( ! $path ) {
@ -226,18 +121,5 @@ class Storage {
return $this->{'Server'};
}
public function to_json() {
$json = array();
foreach ($this->defaults as $key => $value) {
if ( is_callable(array($this, $key)) ) {
$json[$key] = $this->$key();
} else if ( array_key_exists($key, $this) ) {
$json[$key] = $this->{$key};
} else {
$json[$key] = $this->defaults{$key};
}
}
return json_encode($json);
}
} // end class Storage
?>

View File

@ -42,7 +42,7 @@ if ( isset($_REQUEST['object']) and ( $_REQUEST['object'] == 'filter' ) ) {
$filter->delete();
} else {
ZM\Error("No filter id passed when deleting");
ZM\Error('No filter id passed when deleting');
}
} else if ( ( $action == 'Save' ) or ( $action == 'SaveAs' ) or ( $action == 'execute' ) ) {
@ -51,11 +51,30 @@ if ( isset($_REQUEST['object']) and ( $_REQUEST['object'] == 'filter' ) ) {
$_REQUEST['filter']['Query']['sort_asc'] = validStr($_REQUEST['filter']['Query']['sort_asc']);
$_REQUEST['filter']['Query']['limit'] = validInt($_REQUEST['filter']['Query']['limit']);
if ( $action == 'execute' ) {
$tempFilterName = '_TempFilter'.time();
$sql .= ' Name = \''.$tempFilterName.'\'';
} else {
$sql .= ' Name = '.dbEscape($_REQUEST['filter']['Name']);
$_REQUEST['filter']['Name'] = '_TempFilter'.time();
unset($_REQUEST['Id']);
#$tempFilterName = '_TempFilter'.time();
#$sql .= ' Name = \''.$tempFilterName.'\'';
#} else {
#$sql .= ' Name = '.dbEscape($_REQUEST['filter']['Name']);
}
$_REQUEST['filter']['AutoCopy'] = empty($_REQUEST['filter']['AutoCopy']) ? 0 : 1;
$_REQUEST['filter']['AutoMove'] = empty($_REQUEST['filter']['AutoMove']) ? 0 : 1;
$_REQUEST['filter']['AutoArchive'] = empty($_REQUEST['filter']['AutoArchive']) ? 0 : 1;
$_REQUEST['filter']['AutoVideo'] = empty($_REQUEST['filter']['AutoVideo']) ? 0 : 1;
$_REQUEST['filter']['AutoUpload'] = empty($_REQUEST['filter']['AutoUpload']) ? 0 : 1;
$_REQUEST['filter']['AutoEmail'] = empty($_REQUEST['filter']['AutoEmail']) ? 0 : 1;
$_REQUEST['filter']['AutoMessage'] = empty($_REQUEST['filter']['AutoMessage']) ? 0 : 1;
$_REQUEST['filter']['AutoExecute'] = empty($_REQUEST['filter']['AutoExecute']) ? 0 : 1;
$_REQUEST['filter']['AutoDelete'] = empty($_REQUEST['filter']['AutoDelete']) ? 0 : 1;
$_REQUEST['filter']['UpdateDiskSpace'] = empty($_REQUEST['filter']['UpdateDiskSpace']) ? 0 : 1;
$_REQUEST['filter']['Background'] = empty($_REQUEST['filter']['Background']) ? 0 : 1;
$_REQUEST['filter']['Concurrent'] = empty($_REQUEST['filter']['Concurrent']) ? 0 : 1;
$changes = $filter->changes($_REQUEST['filter']);
ZM\Logger::Debug("Changes: " . print_r($changes,true));
if ( 0 ) {
$sql .= ', Query = '.dbEscape(jsonEncode($_REQUEST['filter']['Query']));
$sql .= ', AutoArchive = '.(!empty($_REQUEST['filter']['AutoArchive']) ? 1 : 0);
$sql .= ', AutoVideo = '. ( !empty($_REQUEST['filter']['AutoVideo']) ? 1 : 0);
@ -73,17 +92,25 @@ if ( isset($_REQUEST['object']) and ( $_REQUEST['object'] == 'filter' ) ) {
$sql .= ', UpdateDiskSpace = '. ( !empty($_REQUEST['filter']['UpdateDiskSpace']) ? 1 : 0);
$sql .= ', Background = '. ( !empty($_REQUEST['filter']['Background']) ? 1 : 0);
$sql .= ', Concurrent = '. ( !empty($_REQUEST['filter']['Concurrent']) ? 1 : 0);
}
if ( $_REQUEST['Id'] and ( $action == 'Save' ) ) {
if ( 0 ) {
dbQuery('UPDATE Filters SET '.$sql.' WHERE Id=?', array($_REQUEST['Id']));
}
$filter->save($changes);
if ( $filter->Background() )
$filter->control('stop');
} else {
# COuld be execute
if ( 0 ) {
dbQuery('INSERT INTO Filters SET'.$sql);
$_REQUEST['Id'] = dbInsertId();
$filter = new ZM\Filter($_REQUEST['Id']);
}
$filter->save($changes);
}
if ( !empty($_REQUEST['filter']['Background']) )
if ( $filter->Background() )
$filter->control('start');
if ( $action == 'execute' ) {

View File

@ -44,7 +44,7 @@ if ( !empty($_REQUEST['mid']) && canEdit('Monitors', $_REQUEST['mid']) ) {
$_REQUEST['newZone']['MaxBlobPixels'] = intval(($_REQUEST['newZone']['MaxBlobPixels']*$_REQUEST['newZone']['Area'])/100);
}
unset( $_REQUEST['newZone']['Points'] );
unset($_REQUEST['newZone']['Points']);
# convert these fields to integer e.g. NULL -> 0
$types = array(

View File

@ -1341,7 +1341,7 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&amp;') {
$filter['sql'] .= " IS NOT $value";
break;
default:
ZM\Warning("Invalid operator in filter: " . $term['op'] );
ZM\Warning('Invalid operator in filter: ' . print_r($term['op'], true));
} // end switch op
$filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][op]").'='.urlencode($term['op']);
@ -1472,7 +1472,14 @@ function getPagination( $pages, $page, $maxShortcuts, $query, $querySep='&amp;'
function sortHeader( $field, $querySep='&amp;' ) {
global $view;
return '?view='.$view.$querySep.'page=1'.$_REQUEST['filter']['query'].$querySep.'sort_field='.$field.$querySep.'sort_asc='.($_REQUEST['sort_field'] == $field?!$_REQUEST['sort_asc']:0).$querySep.'limit='.validInt($_REQUEST['limit']);
return implode($querySep, array(
'?view='.$view,
'page=1'.$_REQUEST['filter']['query'],
'sort_field='.$field,
'sort_asc='.($_REQUEST['sort_field'] == $field ? !$_REQUEST['sort_asc'] : 0),
'limit='.validInt($_REQUEST['limit']),
($_REQUEST['eid'] ? 'eid='.$_REQUEST['eid'] : '' ),
));
}
function sortTag( $field ) {
@ -2518,4 +2525,45 @@ function format_duration($time, $separator=':') {
return sprintf('%02d%s%02d%s%02d', floor($time/3600), $separator, ($time/60)%60, $separator, $time%60);
}
function array_recursive_diff($aArray1, $aArray2) {
$aReturn = array();
foreach ($aArray1 as $mKey => $mValue) {
if ( array_key_exists($mKey, $aArray2) ) {
if ( is_array($mValue) ) {
$aRecursiveDiff = array_recursive_diff($mValue, $aArray2[$mKey]);
if ( count($aRecursiveDiff) ) {
$aReturn[$mKey] = $aRecursiveDiff;
}
} else {
if ( $mValue != $aArray2[$mKey] ) {
$aReturn[$mKey] = $mValue;
}
}
} else {
$aReturn[$mKey] = $mValue;
}
}
# Now check for keys in array2 that are not in array1
foreach ($aArray2 as $mKey => $mValue) {
if ( array_key_exists($mKey, $aArray1) ) {
# Already checked it... I think.
#if ( is_array($mValue) ) {
#$aRecursiveDiff = array_recursive_diff($mValue, $aArray2[$mKey]);
#if ( count($aRecursiveDiff) ) {
#$aReturn[$mKey] = $aRecursiveDiff;
#}
#} else {
#if ( $mValue != $aArray2[$mKey] ) {
#$aReturn[$mKey] = $mValue;
#}
#}
} else {
$aReturn[$mKey] = $mValue;
}
}
return $aReturn;
}
?>

View File

@ -132,6 +132,7 @@ $SLANG = array(
'AttrMaxScore' => 'Max. Score',
'AttrMonitorId' => 'Monitor Id',
'AttrMonitorName' => 'Monitor Name',
'AttrSecondaryStorageArea' => 'Secondary Storage Area',
'AttrStorageArea' => 'Storage Area',
'AttrFilterServer' => 'Server Filter is Running On',
'AttrMonitorServer' => 'Server Monitor is Running On',
@ -356,6 +357,7 @@ $SLANG = array(
'FilterArchiveEvents' => 'Archive all matches',
'FilterUpdateDiskSpace' => 'Update used disk space',
'FilterDeleteEvents' => 'Delete all matches',
'FilterCopyEvents' => 'Copy all matches',
'FilterMoveEvents' => 'Move all matches',
'FilterEmailEvents' => 'Email details of all matches',
'FilterExecuteEvents' => 'Execute command on all matches',

View File

@ -286,7 +286,7 @@ function getNavBarHTML($reload = null) {
ZM\Error('Potentially invalid value for ZM_LOG_DATABASE_LIMIT: ' . ZM_LOG_DATABASE_LIMIT);
}
}
echo makePopupLink( '?view=log', 'zmLog', 'log', '<span class="'.logState().'">'.translate('Log').'</span>' );
echo makePopupLink('?view=log', 'zmLog', 'log', '<span class="'.logState().'">'.translate('Log').'</span>');
}
?></li>
<?php
@ -386,9 +386,6 @@ if ( (!ZM_OPT_USE_AUTH) or $user ) {
foreach ( $storage_areas as $area ) {
$storage_paths[$area->Path()] = $area;
}
if ( ! isset($storage_paths[ZM_DIR_EVENTS]) ) {
array_push( $storage_areas, new ZM\Storage() );
}
$func = function($S){
$class = '';
if ( $S->disk_usage_percent() > 98 ) {

View File

@ -134,7 +134,11 @@ if ( ! $Event->Id() ) {
<span id="dataDuration" title="<?php echo translate('Duration') ?>"><?php echo $Event->Length().'s' ?></span>
<span id="dataFrames" title="<?php echo translate('AttrFrames').'/'.translate('AttrAlarmFrames') ?>"><?php echo $Event->Frames() ?>/<?php echo $Event->AlarmFrames() ?></span>
<span id="dataScore" title="<?php echo translate('AttrTotalScore').'/'.translate('AttrAvgScore').'/'.translate('AttrMaxScore') ?>"><?php echo $Event->TotScore() ?>/<?php echo $Event->AvgScore() ?>/<?php echo $Event->MaxScore() ?></span>
<span id="Storage"> <?php echo human_filesize($Event->DiskSpace(null)) . ' on ' . $Event->Storage()->Name() ?></span>
<span id="Storage">
<?php echo
human_filesize($Event->DiskSpace(null)) . ' on ' . $Event->Storage()->Name().
( $Event->SecondaryStorageId() ? ', ' . $Event->SecondaryStorage()->Name() :'' )
?></span>
<div id="closeWindow"><a href="#" onclick="<?php echo $popup ? 'window.close()' : 'window.history.back();return false;' ?>"><?php echo $popup ? translate('Close') : translate('Back') ?></a></div>
</div>
<div id="menuBar1">

View File

@ -22,36 +22,32 @@ if ( !canView('Events') ) {
$view = 'error';
return;
}
require_once 'includes/Filter.php';
require_once('includes/Object.php');
require_once('includes/Storage.php');
require_once('includes/Filter.php');
parseSort();
$filterNames = array( ''=>translate('ChooseFilter') );
$filterNames = array(''=>translate('ChooseFilter'));
$filter = NULL;
foreach ( dbFetchAll('SELECT * FROM Filters ORDER BY Name') as $row ) {
$filterNames[$row['Id']] = $row['Id'] . ' ' . $row['Name'];
if ( $row['Background'] )
$filterNames[$row['Id']] .= '*';
if ( $row['Concurrent'] )
$filterNames[$row['Id']] .= '&';
foreach ( ZM\Filter::find(null,array('order'=>'lower(Name)')) as $Filter ) {
$filterNames[$Filter->Id()] = $Filter->Id() . ' ' . $Filter->Name();
if ( $Filter->Background() )
$filterNames[$Filter->Id()] .= '*';
if ( $Filter->Concurrent() )
$filterNames[$Filter->Id()] .= '&';
if ( isset($_REQUEST['Id']) && $_REQUEST['Id'] == $row['Id'] ) {
$filter = new ZM\Filter($row);
if ( isset($_REQUEST['Id']) && ($_REQUEST['Id'] == $Filter->Id()) ) {
$filter = $Filter;
}
}
if ( ! $filter ) {
if ( !$filter ) {
$filter = new ZM\Filter();
}
if ( isset($_REQUEST['sort_field']) && isset($_REQUEST['filter']) ) {
$_REQUEST['filter']['Query']['sort_field'] = $_REQUEST['sort_field'];
$_REQUEST['filter']['Query']['sort_asc'] = $_REQUEST['sort_asc'];
$_REQUEST['filter']['Query']['limit'] = $_REQUEST['limit'];
}
if ( isset($_REQUEST['filter']) ) {
$filter->set($_REQUEST['filter']);
# Update our filter object with whatever changes we have made before saving
#$filter->set($_REQUEST['filter']);
}
$conjunctionTypes = getFilterQueryConjunctionTypes();
@ -97,12 +93,13 @@ $attrTypes = array(
'DiskPercent' => translate('AttrDiskPercent'),
'DiskSpace' => translate('AttrDiskSpace'),
'SystemLoad' => translate('AttrSystemLoad'),
'StorageId' => translate('AttrStorageArea'),
'ServerId' => translate('AttrMonitorServer'),
'StorageId' => translate('AttrStorageArea'),
'SecondaryStorageId' => translate('AttrSecondaryStorageArea'),
'ServerId' => translate('AttrMonitorServer'),
'FilterServerId' => translate('AttrFilterServer'),
'MonitorServerId' => translate('AttrMonitorServer'),
'StorageServerId' => translate('AttrStorageServer'),
'StateId' => translate('AttrStateId'),
'StateId' => translate('AttrStateId'),
);
$opTypes = array(
@ -127,27 +124,24 @@ $archiveTypes = array(
$focusWindow = true;
$storageareas = array('' => 'All');
//$storageareas[0] = 'Default ' . ZM_DIR_EVENTS;
foreach ( dbFetchAll('SELECT Id,Name FROM Storage ORDER BY lower(Name) ASC') as $storage ) {
$storageareas[$storage['Id']] = $storage['Name'];
}
$storageareas = array('' => 'All') + ZM\ZM_Object::Objects_Indexed_By_Id('ZM\Storage');
$weekdays = array();
for ( $i = 0; $i < 7; $i++ ) {
$weekdays[$i] = strftime('%A', mktime(12, 0, 0, 1, $i+1, 2001));
}
$states = array();
foreach ( dbFetchAll('SELECT Id, Name FROM States ORDER BY lower(Name) ASC') as $state_row ) {
foreach ( dbFetchAll('SELECT `Id`, `Name` FROM `States` ORDER BY lower(`Name`) ASC') as $state_row ) {
$states[$state_row['Id']] = validHtmlStr($state_row['Name']);
}
$servers = array();
$servers['ZM_SERVER_ID'] = 'Current Server';
$servers['NULL'] = 'No Server';
foreach ( dbFetchAll('SELECT Id, Name FROM Servers ORDER BY lower(Name) ASC') as $server ) {
foreach ( dbFetchAll('SELECT `Id`, `Name` FROM `Servers` ORDER BY lower(`Name`) ASC') as $server ) {
$servers[$server['Id']] = validHtmlStr($server['Name']);
}
$monitors = array();
foreach ( dbFetchAll('SELECT Id, Name FROM Monitors ORDER BY Name ASC') as $monitor ) {
foreach ( dbFetchAll('SELECT `Id`, `Name` FROM `Monitors` ORDER BY lower(`Name`) ASC') as $monitor ) {
if ( visibleMonitor($monitor['Id']) ) {
$monitors[$monitor['Name']] = validHtmlStr($monitor['Name']);
}
@ -273,7 +267,7 @@ for ( $i=0; $i < count($terms); $i++ ) {
<td><?php echo htmlSelect("filter[Query][terms][$i][op]", $opTypes, $term['op']); ?></td>
<td><?php echo htmlSelect("filter[Query][terms][$i][val]", $servers, $term['val']); ?></td>
<?php
} elseif ( $term['attr'] == 'StorageId' ) {
} elseif ( ($term['attr'] == 'StorageId') || ($term['attr'] == 'SecondaryStorageId') ) {
?>
<td><?php echo htmlSelect("filter[Query][terms][$i][op]", $opTypes, $term['op']); ?></td>
<td><?php echo htmlSelect("filter[Query][terms][$i][val]", $storageareas, $term['val']); ?></td>
@ -391,7 +385,13 @@ if ( ZM_OPT_MESSAGE ) {
<label><?php echo translate('FilterDeleteEvents') ?></label>
<input type="checkbox" name="filter[AutoDelete]" value="1"<?php if ( $filter->AutoDelete() ) { ?> checked="checked"<?php } ?> data-on-click-this="updateButtons"/>
</p>
<p><label><?php echo translate('FilterMoveEvents') ?></label>
<p>
<label><?php echo translate('FilterCopyEvents') ?></label>
<input type="checkbox" name="filter[AutoCopy]" value="1"<?php if ( $filter->AutoCopy() ) { ?> checked="checked"<?php } ?> data-on-click-this="click_autocopy"/>
<?php echo htmlSelect('filter[AutoCopyTo]', $storageareas, $filter->AutoCopyTo(), $filter->AutoCopy() ? null : array('style'=>'display:none;')); ?>
</p>
<p>
<label><?php echo translate('FilterMoveEvents') ?></label>
<input type="checkbox" name="filter[AutoMove]" value="1"<?php if ( $filter->AutoMove() ) { ?> checked="checked"<?php } ?> data-on-click-this="click_automove"/>
<?php echo htmlSelect('filter[AutoMoveTo]', $storageareas, $filter->AutoMoveTo(), $filter->AutoMove() ? null : array('style'=>'display:none;')); ?>
</p>

View File

@ -39,7 +39,7 @@ if ( empty($_REQUEST['sort_field']) )
if ( !isset($_REQUEST['sort_asc']) )
$_REQUEST['sort_asc'] = true;
if( ! isset($_REQUEST['filter'])){
if ( ! isset($_REQUEST['filter'])){
// generate a dummy filter from the eid for pagination
$_REQUEST['filter'] = array('Query' => array( 'terms' => array( ) ) );
$_REQUEST['filter'] = addFilterTerm(
@ -53,7 +53,6 @@ parseSort();
parseFilter($_REQUEST['filter']);
$filterQuery = $_REQUEST['filter']['query'];
if ( $_REQUEST['filter']['sql'] ) {
$countSql .= $_REQUEST['filter']['sql'];
$frameSql .= $_REQUEST['filter']['sql'];
@ -61,7 +60,6 @@ if ( $_REQUEST['filter']['sql'] ) {
$frameSql .= " ORDER BY $sortColumn $sortOrder,Id $sortOrder";
if ( isset( $_REQUEST['scale'] ) ) {
$scale = validNum($_REQUEST['scale']);
} else if ( isset( $_COOKIE['zmWatchScale'.$Monitor->Id()] ) ) {
@ -75,7 +73,7 @@ if ( isset( $_REQUEST['scale'] ) ) {
$page = isset($_REQUEST['page']) ? validInt($_REQUEST['page']) : 1;
$limit = isset($_REQUEST['limit']) ? validInt($_REQUEST['limit']) : 0;
$nFrames = dbFetchOne($countSql, 'FrameCount' );
$nFrames = dbFetchOne($countSql, 'FrameCount');
if ( !empty($limit) && ($nFrames > $limit) ) {
$nFrames = $limit;

View File

@ -72,6 +72,15 @@ function click_automove(element) {
}
}
function click_autocopy(element) {
updateButtons(this);
if ( this.checked ) {
$j(this.form.elements['filter[AutoCopyTo]']).css('display', 'inline');
} else {
this.form.elements['filter[AutoCopyTo]'].hide();
}
}
function checkValue( element ) {
var rows = $j(element).closest('tbody').children();
parseRows(rows);
@ -200,10 +209,10 @@ function parseRows(rows) {
}
var serverVal = inputTds.eq(4).children().val();
inputTds.eq(4).html(serverSelect).children().val(serverVal).chosen({width: "101%"});
} else if ( attr == 'StorageId' ) { //Choose by storagearea
} else if ( (attr == 'StorageId') || (attr == 'SecondaryStorageId') ) { //Choose by storagearea
var storageSelect = $j('<select></select>').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]');
for ( key in storageareas ) {
storageSelect.append('<option value="' + key + '">' + storageareas[key] + '</option>');
storageSelect.append('<option value="' + key + '">' + storageareas[key].Name + '</option>');
}
var storageVal = inputTds.eq(4).children().val();
inputTds.eq(4).html(storageSelect).children().val(storageVal).chosen({width: "101%"});

View File

@ -371,8 +371,8 @@ function updateY( index ) {
function saveChanges( element ) {
var form = element.form;
if ( validateForm( form ) ) {
submitForm( form );
if ( validateForm(form) ) {
submitForm(form);
if ( form.elements['newZone[Type]'].value == 'Privacy' ) {
alert( 'Capture process for this monitor will be restarted for the Privacy zone changes to take effect.' );
}

View File

@ -63,7 +63,7 @@ $maxY = $monitor->Height()-1;
if ( !isset($newZone) ) {
if ( $zid > 0 ) {
$zone = dbFetchOne( 'SELECT * FROM Zones WHERE MonitorId = ? AND Id=?', NULL, array( $monitor->Id(), $zid ) );
$zone = dbFetchOne('SELECT * FROM Zones WHERE MonitorId = ? AND Id=?', NULL, array($monitor->Id(), $zid));
} else {
$zone = array(
'Id' => 0,
@ -98,23 +98,23 @@ if ( !isset($newZone) ) {
} # end if new Zone
# Ensure Zone fits within the limits of the Monitor
limitPoints( $newZone['Points'], $minX, $minY, $maxX, $maxY );
limitPoints($newZone['Points'], $minX, $minY, $maxX, $maxY);
ksort( $newZone['Points'], SORT_NUMERIC );
ksort($newZone['Points'], SORT_NUMERIC);
$newZone['Coords'] = pointsToCoords( $newZone['Points'] );
$newZone['Area'] = getPolyArea( $newZone['Points'] );
$newZone['AreaCoords'] = preg_replace( '/\s+/', ',', $newZone['Coords'] );
$selfIntersecting = isSelfIntersecting( $newZone['Points'] );
$newZone['Coords'] = pointsToCoords($newZone['Points']);
$newZone['Area'] = getPolyArea($newZone['Points']);
$newZone['AreaCoords'] = preg_replace('/\s+/', ',', $newZone['Coords']);
$selfIntersecting = isSelfIntersecting($newZone['Points']);
$focusWindow = true;
$connkey = generateConnKey();
$streamSrc = '';
$streamMode = '';
# Have to do this here, because the .js.php references somethings figured out when generating the streamHTML
$StreamHTML = getStreamHTML( $monitor, array('scale'=>$scale) );
$StreamHTML = getStreamHTML($monitor, array('scale'=>$scale));
xhtmlHeaders(__FILE__, translate('Zone') );
xhtmlHeaders(__FILE__, translate('Zone'));
?>
<body>
<div id="page">
@ -132,7 +132,7 @@ xhtmlHeaders(__FILE__, translate('Zone') );
<input type="hidden" name="newZone[Area]" value="<?php echo $newZone['Area'] ?>"/>
<input type="hidden" name="newZone[AlarmRGB]" value=""/>
<div id="settingsPanel">
<table id="zoneSettings" cellspacing="0">
<table id="zoneSettings">
<tbody>
<tr>
<th scope="row"><?php echo translate('Name') ?></th>
@ -162,7 +162,7 @@ xhtmlHeaders(__FILE__, translate('Zone') );
</tr>
<tr>
<th scope="row"><?php echo translate('CheckMethod') ?></th>
<td colspan="2"><?php echo buildSelect( "newZone[CheckMethod]", $optCheckMethods, 'applyCheckMethod()' ) ?></td>
<td colspan="2"><?php echo buildSelect('newZone[CheckMethod]', $optCheckMethods, 'applyCheckMethod()' ) ?></td>
</tr>
<tr>
<th scope="row"><?php echo translate('ZoneMinMaxPixelThres') ?></th>
@ -216,14 +216,14 @@ xhtmlHeaders(__FILE__, translate('Zone') );
<svg id="zoneSVG" class="zones" style="position: absolute; top: 0; left: 0; width: <?php echo reScale( $monitor->Width(), $scale ) ?>px; height: <?php echo reScale( $monitor->Height(), $scale ) ?>px; background: none;">
<?php
if ( $zone['Id'] ) {
$other_zones = dbFetchAll( 'SELECT * FROM Zones WHERE MonitorId = ? AND Id != ?', NULL, array( $monitor->Id(), $zone['Id'] ) );
$other_zones = dbFetchAll('SELECT * FROM Zones WHERE MonitorId = ? AND Id != ?', NULL, array($monitor->Id(), $zone['Id']));
} else {
$other_zones = dbFetchAll( 'SELECT * FROM Zones WHERE MonitorId = ?', NULL, array( $monitor->Id() ) );
$other_zones = dbFetchAll('SELECT * FROM Zones WHERE MonitorId = ?', NULL, array($monitor->Id()));
}
if ( count( $other_zones ) ) {
if ( count($other_zones) ) {
$html = '';
foreach( $other_zones as $other_zone ) {
$other_zone['AreaCoords'] = preg_replace( '/\s+/', ',', $other_zone['Coords'] );
foreach ( $other_zones as $other_zone ) {
$other_zone['AreaCoords'] = preg_replace('/\s+/', ',', $other_zone['Coords']);
$html .= '<polygon id="zonePoly'.$other_zone['Id'].'" points="'. $other_zone['AreaCoords'] .'" class="'. $other_zone['Type'] .'"/>';
}
echo $html;
@ -267,9 +267,11 @@ for ( $i = 0; $i < $pointCols; $i++ ) {
</tr>
</tbody>
</table>
<input id="pauseBtn" type="button" value="<?php echo translate('Pause') ?>" data-on-click="streamCmdPauseToggle"/>
<input type="submit" id="submitBtn" name="submitBtn" value="<?php echo translate('Save') ?>" onclick="return saveChanges( this )"<?php if (!canEdit( 'Monitors' ) || (false && $selfIntersecting)) { ?> disabled="disabled"<?php } ?>/>
<input type="button" value="<?php echo translate('Cancel') ?>" onclick="refreshParentWindow(); closeWindow();"/>
<button id="pauseBtn" type="button" data-on-click="streamCmdPauseToggle"><?php echo translate('Pause') ?></button>
<button type="button" id="submitBtn" name="submitBtn" value="Save" data-on-click-this="saveChanges"<?php if (!canEdit('Monitors') || (false && $selfIntersecting)) { ?> disabled="disabled"<?php } ?>>
<?php echo translate('Save') ?>
</button>
<button type="button" value="Cancel" onclick="refreshParentWindow(); closeWindow();"><?php echo translate('Cancel') ?></button>
</div>
</form>
</div>