From e821553265ed10a73b4f6d20d3162c2a83b07bed Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Tue, 23 Jul 2019 09:54:39 -0400
Subject: [PATCH 01/28] Split MoveTo into CopyTo and MoveTo.
---
scripts/ZoneMinder/lib/ZoneMinder/Event.pm | 126 +++++++++++----------
1 file changed, 69 insertions(+), 57 deletions(-)
diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm
index 9c6fe3cd5..e1516f1f5 100644
--- a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm
+++ b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm
@@ -393,61 +393,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 +524,7 @@ sub DiskSpace {
return $_[0]{DiskSpace};
}
-sub MoveTo {
+sub CopyTo {
my ( $self, $NewStorage ) = @_;
my $OldStorage = $self->Storage(undef);
@@ -559,7 +564,7 @@ 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;
@@ -650,7 +655,7 @@ Debug("Files to move @files");
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");
+ 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 +663,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 +681,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
From f9b5c8a1f4dcfeb2af820309113d45095db23eea Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Tue, 23 Jul 2019 09:55:14 -0400
Subject: [PATCH 02/28] If query is empty don't bother parsing it
---
scripts/ZoneMinder/lib/ZoneMinder/Filter.pm | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm
index 8383e43b7..1c0dd151b 100644
--- a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm
+++ b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm
@@ -132,6 +132,12 @@ sub Sql {
my $self = shift;
$$self{Sql} = shift if @_;
if ( ! $$self{Sql} ) {
+ $self->{Sql} = '';
+ if ( ! $self->{Query} ) {
+ Warning("No query in Filter!");
+ return;
+ }
+
my $filter_expr = ZoneMinder::General::jsonDecode($self->{Query});
my $sql = 'SELECT E.*,
unix_timestamp(E.StartTime) as Time,
@@ -142,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}} ) {
From fd95ab23e9a78b116b621bd4498a5701dc2b83fd Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Tue, 23 Jul 2019 09:55:27 -0400
Subject: [PATCH 03/28] Add AutoCopy support
---
scripts/zmfilter.pl.in | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in
index b572d40fa..0b8812c87 100644
--- a/scripts/zmfilter.pl.in
+++ b/scripts/zmfilter.pl.in
@@ -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,11 @@ sub checkFilter {
$_ = $Event->MoveTo($NewStorage);
Error($_) if $_;
}
+ if ( $filter->{AutoCopy} ) {
+ my $NewStorage = new ZoneMinder::Storage($filter->{AutoCopyTo});
+ $_ = $Event->CopyTo($NewStorage);
+ Error($_) if $_;
+ }
if ( $filter->{UpdateDiskSpace} ) {
$ZoneMinder::Database::dbh->begin_work();
From b05aff1d5d5e4a339d5f6731dcffe2dfde679ce9 Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Tue, 23 Jul 2019 09:57:16 -0400
Subject: [PATCH 04/28] Update Filter Object to extend ZM_Object. Rename Query
to Query_json and implement a Query function to parse Query_json
---
web/includes/Filter.php | 169 ++++++++++------------------------------
1 file changed, 41 insertions(+), 128 deletions(-)
diff --git a/web/includes/Filter.php b/web/includes/Filter.php
index 8da602539..f8b06d4e5 100644
--- a/web/includes/Filter.php
+++ b/web/includes/Filter.php
@@ -1,9 +1,11 @@
null,
'Name' => '',
'AutoExecute' => 0,
@@ -16,63 +18,44 @@ 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,
-);
+ #'limit' => 100,
+ 'Query_json' => '',
+ #'sort_field' => ZM_WEB_EVENT_SORT_FIELD,
+ #'sort_asc' => ZM_WEB_EVENT_SORT_ORDER,
+ );
- 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($new = -1) {
+ if ( $new and ( $new != -1 ) ) {
+ $this->{'Query'} = $new;
+ $this->{'Query_json'} = jsonEncode($new);
+ Logger::Debug("Setting Query to " . $this->{'Query_json'});
+ }
+ if ( !array_key_exists('Query', $this) ) {
+ if ( array_key_exists('Query_json', $this) and $this->{'Query_json'} ) {
+ $this->{'Query'} = jsonDecode($this->{'Query_json'});
+ Logger::Debug("Decoded Query already" . print_r($this->{'Query'}, true ));
- if ( $row ) {
- foreach ($row as $k => $v) {
- $this->{$k} = $v;
- }
- if ( array_key_exists('Query', $this) and $this->{'Query'} ) {
- $this->{'Query'} = jsonDecode($this->{'Query'});
} else {
+ Logger::Debug("No Have Query_json already");
$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" );
+ Logger::Debug("Have Query already" . print_r($this->{'Query'}, true ));
}
+ 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( ) {
@@ -93,101 +76,31 @@ public $defaults = array(
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);
+ $this->Query()['sort_asc'] = func_get_arg(0);
}
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);
}
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();
if ( !count($Servers) and !$server_id ) {
From 7c52f8a4aea74f9541830a9402c7a702d901405a Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Tue, 23 Jul 2019 09:57:44 -0400
Subject: [PATCH 05/28] Fixes and add Objects_Indexed_By_Id
---
web/includes/Object.php | 230 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 230 insertions(+)
create mode 100644 web/includes/Object.php
diff --git a/web/includes/Object.php b/web/includes/Object.php
new file mode 100644
index 000000000..041b45bed
--- /dev/null
+++ b/web/includes/Object.php
@@ -0,0 +1,230 @@
+ $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 ( $this->defaults as $field=>$default_value ) {
+ if ( array_key_exists($field, $new_values) ) {
+ Logger::Debug("Checking default $field => $default_value exists in new values :".$this->{$field} . " " .$new_values[$field]);
+ 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
+?>
From 35ec60ca031f5ee6e3a72d9d53ae2cfe2221b1a4 Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Tue, 23 Jul 2019 09:58:05 -0400
Subject: [PATCH 06/28] Change Storage object to extend ZM_Object
---
web/includes/Storage.php | 136 +++------------------------------------
1 file changed, 9 insertions(+), 127 deletions(-)
diff --git a/web/includes/Storage.php b/web/includes/Storage.php
index b3caa3ffa..8607b52fe 100644
--- a/web/includes/Storage.php
+++ b/web/includes/Storage.php
@@ -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
?>
From 9b6dedb35d4399bc7e8fc13083d103e1d1ad766f Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Tue, 23 Jul 2019 09:58:28 -0400
Subject: [PATCH 07/28] Update Filter saving action to use object set/save etc
---
web/includes/actions/filter.php | 37 ++++++++++++++++++++++++++++-----
1 file changed, 32 insertions(+), 5 deletions(-)
diff --git a/web/includes/actions/filter.php b/web/includes/actions/filter.php
index 6b3019ee5..8548f21d1 100644
--- a/web/includes/actions/filter.php
+++ b/web/includes/actions/filter.php
@@ -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' ) {
From 346933126d822e89d0c75d21545f2ac108a5364f Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Tue, 23 Jul 2019 09:59:49 -0400
Subject: [PATCH 08/28] Update filter view to use Filter::find
---
web/skins/classic/views/filter.php | 61 ++++++++++++++++--------------
1 file changed, 32 insertions(+), 29 deletions(-)
diff --git a/web/skins/classic/views/filter.php b/web/skins/classic/views/filter.php
index 1eb4b71c3..893d1ecbd 100644
--- a/web/skins/classic/views/filter.php
+++ b/web/skins/classic/views/filter.php
@@ -22,36 +22,35 @@ 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'];
-}
+ZM\Logger::Debug("Query: " . $filter->Query_json());
+ZM\Logger::Debug("Query: " . print_r($filter->Query(), true));
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 +96,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 +127,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']);
}
@@ -391,7 +388,13 @@ if ( ZM_OPT_MESSAGE ) {
AutoDelete() ) { ?> checked="checked" data-on-click-this="updateButtons"/>
-
+
+
+ AutoCopy() ) { ?> checked="checked" data-on-click-this="click_autocopy"/>
+ AutoCopyTo(), $filter->AutoCopy() ? null : array('style'=>'display:none;')); ?>
+
+
+
AutoMove() ) { ?> checked="checked" data-on-click-this="click_automove"/>
AutoMoveTo(), $filter->AutoMove() ? null : array('style'=>'display:none;')); ?>
From 0e040fc2fcbafd57682f319f577ceea47e2bfea3 Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Tue, 23 Jul 2019 10:00:05 -0400
Subject: [PATCH 09/28] Add click_autocopy function
---
web/skins/classic/views/js/filter.js | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/web/skins/classic/views/js/filter.js b/web/skins/classic/views/js/filter.js
index 49a882ee6..36d1e09d5 100644
--- a/web/skins/classic/views/js/filter.js
+++ b/web/skins/classic/views/js/filter.js
@@ -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);
From 88beb46c3e645491d93c157be5d6af10b37d16c8 Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Tue, 23 Jul 2019 10:04:15 -0400
Subject: [PATCH 10/28] Add FilterCopyEvents
---
web/lang/en_gb.php | 1 +
1 file changed, 1 insertion(+)
diff --git a/web/lang/en_gb.php b/web/lang/en_gb.php
index 4001c456e..526486921 100644
--- a/web/lang/en_gb.php
+++ b/web/lang/en_gb.php
@@ -356,6 +356,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',
From bb653b172c59d37e63844494a933e853e7cc94ca Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Tue, 23 Jul 2019 14:34:26 -0400
Subject: [PATCH 11/28] Use hires time to give better bandwidth reporitng
---
scripts/ZoneMinder/lib/ZoneMinder/Event.pm | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm
index e1516f1f5..27c157359 100644
--- a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm
+++ b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm
@@ -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);
@@ -595,7 +596,7 @@ Debug("Files to move @files");
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 ( ! $size ) {
@@ -607,10 +608,10 @@ Debug("Files to move @files");
}
my $filename = $event_path.'/'.File::Basename::basename($file);
- if ( ! $bucket->add_key( $filename, $file_contents ) ) {
+ if ( ! $bucket->add_key($filename, $file_contents) ) {
die "Unable to add key for $filename";
}
- my $duration = time - $starttime;
+ 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.
@@ -621,13 +622,13 @@ Debug("Files to move @files");
} # 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";
@@ -641,20 +642,20 @@ 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;
+ 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
From 98922b6788dfdc1f1d58a608fa75c66ecfd0e2f2 Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Wed, 24 Jul 2019 09:37:16 -0400
Subject: [PATCH 12/28] Add SecondaryStorageId to Event so that we can update
it
---
scripts/ZoneMinder/lib/ZoneMinder/Event.pm | 29 ++++++++++++----------
1 file changed, 16 insertions(+), 13 deletions(-)
diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm
index 27c157359..fd1c8297d 100644
--- a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm
+++ b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm
@@ -64,6 +64,7 @@ $serial = $primary_key = 'Id';
Id
MonitorId
StorageId
+ SecondaryStorageId
Name
Cause
StartTime
@@ -117,7 +118,7 @@ sub Time {
}
sub getPath {
- return Path( @_ );
+ return Path(@_);
}
sub Path {
@@ -132,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};
}
@@ -164,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())
),
);
@@ -204,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}
@@ -256,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;
@@ -283,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 ) {
@@ -537,9 +540,9 @@ sub CopyTo {
# 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 ) {
@@ -551,7 +554,7 @@ sub CopyTo {
# 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} ) {
@@ -586,13 +589,13 @@ sub CopyTo {
}
my $event_path = 'events/'.$self->RelativePath();
-Info("Making dir ectory $event_path/");
+ 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");
+ Debug("Files to move @files");
for my $file (@files) {
next if $file =~ /^\./;
( $file ) = ( $file =~ /^(.*)$/ ); # De-taint
From 99f78c50af9a981276e29a6fab92c4b0a48f3dfb Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Wed, 24 Jul 2019 09:37:38 -0400
Subject: [PATCH 13/28] Add Updating SecondaryStorageId when using CopyTo
---
scripts/zmfilter.pl.in | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in
index 0b8812c87..b3da3bb68 100644
--- a/scripts/zmfilter.pl.in
+++ b/scripts/zmfilter.pl.in
@@ -346,10 +346,20 @@ sub checkFilter {
Error($_) if $_;
}
if ( $filter->{AutoCopy} ) {
- my $NewStorage = new ZoneMinder::Storage($filter->{AutoCopyTo});
- $_ = $Event->CopyTo($NewStorage);
- Error($_) if $_;
- }
+ # 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($filter->{AutoCopyTo});
+ if ( $NewStorage ) {
+ $_ = $Event->CopyTo($NewStorage);
+ if ( $_ ) {
+ Error($_);
+ } else {
+ $Event->save({SecondaryStorageId=>$$NewStorage{Id}});
+ }
+ } 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();
@@ -368,7 +378,7 @@ sub checkFilter {
$ZoneMinder::Database::dbh->commit();
} # end if UpdateDiskSpace
} # end foreach event
-}
+} # end sub checkFilter
sub generateVideo {
my $filter = shift;
From 2d556e6402b9430ff156abd8e441f0b03add487e Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Wed, 24 Jul 2019 10:52:32 -0400
Subject: [PATCH 14/28] Add SecondaryStorageId to Events
---
db/zm_create.sql.in | 1 +
1 file changed, 1 insertion(+)
diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in
index d1952f66a..113d434ff 100644
--- a/db/zm_create.sql.in
+++ b/db/zm_create.sql.in
@@ -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,
From 57133691e91c8898bd3700c3a68908fdc02f1349 Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Wed, 24 Jul 2019 10:53:23 -0400
Subject: [PATCH 15/28] Add update script for SecondaryStorageArea capability
in Events and Filters
---
db/zm_update-1.33.14.sql | 51 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
create mode 100644 db/zm_update-1.33.14.sql
diff --git a/db/zm_update-1.33.14.sql b/db/zm_update-1.33.14.sql
new file mode 100644
index 000000000..83d0cfbba
--- /dev/null
+++ b/db/zm_update-1.33.14.sql
@@ -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;
From afa02e436d6bddffa202fada5f74162d5dda4730 Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Wed, 24 Jul 2019 10:53:56 -0400
Subject: [PATCH 16/28] Upgrade Storage perl object to use parent Object::find
---
scripts/ZoneMinder/lib/ZoneMinder/Storage.pm | 47 +-------------------
1 file changed, 2 insertions(+), 45 deletions(-)
diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Storage.pm b/scripts/ZoneMinder/lib/ZoneMinder/Storage.pm
index 1f7c1b9fe..17d196f92 100644
--- a/scripts/ZoneMinder/lib/ZoneMinder/Storage.pm
+++ b/scripts/ZoneMinder/lib/ZoneMinder/Storage.pm
@@ -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 ) {
From 58851d23d2b4780f2da15f7e5f26cb033edeef30 Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Wed, 24 Jul 2019 11:22:55 -0400
Subject: [PATCH 17/28] Add Secondary Storage support to the Event object
---
web/includes/Event.php | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/web/includes/Event.php b/web/includes/Event.php
index 1b996a839..dc7dd3575 100644
--- a/web/includes/Event.php
+++ b/web/includes/Event.php
@@ -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'}));
From aff081ad41c127133737fb4d7feb7add9fb39539 Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Wed, 24 Jul 2019 11:23:13 -0400
Subject: [PATCH 18/28] Must commit after COpyTo to release locks
---
scripts/zmfilter.pl.in | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in
index b3da3bb68..5cba8f1d4 100644
--- a/scripts/zmfilter.pl.in
+++ b/scripts/zmfilter.pl.in
@@ -348,13 +348,15 @@ sub checkFilter {
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($filter->{AutoCopyTo});
+ 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}");
From 341f4adbdfaedd093869a009754f46a0a2c6f58d Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Wed, 24 Jul 2019 11:23:38 -0400
Subject: [PATCH 19/28] Functions that change the Query must reset Query_json
as well
---
web/includes/Filter.php | 46 ++++++++++++++++++++++++++---------------
1 file changed, 29 insertions(+), 17 deletions(-)
diff --git a/web/includes/Filter.php b/web/includes/Filter.php
index f8b06d4e5..d59ecda7c 100644
--- a/web/includes/Filter.php
+++ b/web/includes/Filter.php
@@ -23,29 +23,33 @@ class Filter extends ZM_Object {
'UpdateDiskSpace' => 0,
'Background' => 0,
'Concurrent' => 0,
- #'limit' => 100,
'Query_json' => '',
- #'sort_field' => ZM_WEB_EVENT_SORT_FIELD,
- #'sort_asc' => ZM_WEB_EVENT_SORT_ORDER,
);
- public function Query($new = -1) {
- if ( $new and ( $new != -1 ) ) {
- $this->{'Query'} = $new;
- $this->{'Query_json'} = jsonEncode($new);
- Logger::Debug("Setting Query to " . $this->{'Query_json'});
+ 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'};
+ }
+
+ 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'});
- Logger::Debug("Decoded Query already" . print_r($this->{'Query'}, true ));
-
} else {
- Logger::Debug("No Have Query_json already");
$this->{'Query'} = array();
}
} else {
- Logger::Debug("Have Query already" . print_r($this->{'Query'}, true ));
+ 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'};
}
@@ -59,8 +63,10 @@ class Filter extends ZM_Object {
}
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'];
@@ -71,7 +77,9 @@ class Filter extends ZM_Object {
// 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'];
@@ -82,7 +90,9 @@ class Filter extends ZM_Object {
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'];
@@ -93,7 +103,9 @@ class Filter extends ZM_Object {
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'];
From e3a9d5d48875c6cf8e24f73dc9f0d880a49e0a08 Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Wed, 24 Jul 2019 11:24:14 -0400
Subject: [PATCH 20/28] Rewrite changes to run through the keys of the passed
in new values array, and handle object methods as well as basic values
---
web/includes/Object.php | 46 +++++++++++++++++++++++++++++++----------
1 file changed, 35 insertions(+), 11 deletions(-)
diff --git a/web/includes/Object.php b/web/includes/Object.php
index 041b45bed..2b58928d9 100644
--- a/web/includes/Object.php
+++ b/web/includes/Object.php
@@ -171,19 +171,43 @@ class ZM_Object {
public function changes( $new_values ) {
$changes = array();
- foreach ( $this->defaults as $field=>$default_value ) {
- if ( array_key_exists($field, $new_values) ) {
- Logger::Debug("Checking default $field => $default_value exists in new values :".$this->{$field} . " " .$new_values[$field]);
- 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]] );
+ 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;
}
- } else {
- Logger::Debug("Checking default $field => $default_value not in new_values");
}
+
+ #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
From 45afc2a534878b86ae10928fc4241c1f9acad36c Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Wed, 24 Jul 2019 11:24:37 -0400
Subject: [PATCH 21/28] introduce array_recursive_diff which we use to compare
two arrays in Object::changes
---
web/includes/functions.php | 41 ++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/web/includes/functions.php b/web/includes/functions.php
index 867861ffe..9e0655933 100644
--- a/web/includes/functions.php
+++ b/web/includes/functions.php
@@ -2518,4 +2518,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;
+}
+
?>
From 1254e8ab67d0c90ee0b6b98b3496e5ad70036c78 Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Wed, 24 Jul 2019 11:24:50 -0400
Subject: [PATCH 22/28] Add AttrSecondaryStorageArea to lang
---
web/lang/en_gb.php | 1 +
1 file changed, 1 insertion(+)
diff --git a/web/lang/en_gb.php b/web/lang/en_gb.php
index 526486921..0e575fd1f 100644
--- a/web/lang/en_gb.php
+++ b/web/lang/en_gb.php
@@ -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',
From 1a0beab70336397109379197de3b253c01d2642c Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Wed, 24 Jul 2019 11:25:38 -0400
Subject: [PATCH 23/28] add Secondary Storage Area options. Storage array is
now an array of Objects so use the Name key
---
web/skins/classic/views/js/filter.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/web/skins/classic/views/js/filter.js b/web/skins/classic/views/js/filter.js
index 36d1e09d5..99e8a9c03 100644
--- a/web/skins/classic/views/js/filter.js
+++ b/web/skins/classic/views/js/filter.js
@@ -209,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('').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]');
for ( key in storageareas ) {
- storageSelect.append('');
+ storageSelect.append('');
}
var storageVal = inputTds.eq(4).children().val();
inputTds.eq(4).html(storageSelect).children().val(storageVal).chosen({width: "101%"});
From 2d46f2adaba3747401d96cd8549916a739e8d696 Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Wed, 24 Jul 2019 11:25:51 -0400
Subject: [PATCH 24/28] add Secondary Storage Area options.
---
web/skins/classic/views/filter.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/web/skins/classic/views/filter.php b/web/skins/classic/views/filter.php
index 893d1ecbd..2f159230e 100644
--- a/web/skins/classic/views/filter.php
+++ b/web/skins/classic/views/filter.php
@@ -270,7 +270,7 @@ for ( $i=0; $i < count($terms); $i++ ) {
|
|
|
|
From 39262d55f5914004a2ae41a273bb7631d96294ec Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Wed, 24 Jul 2019 11:26:07 -0400
Subject: [PATCH 25/28] Also show secondary storage area when viewing event
---
web/skins/classic/views/event.php | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/web/skins/classic/views/event.php b/web/skins/classic/views/event.php
index b09f88acc..f0a6e343c 100644
--- a/web/skins/classic/views/event.php
+++ b/web/skins/classic/views/event.php
@@ -134,7 +134,11 @@ if ( ! $Event->Id() ) {
Length().'s' ?>
Frames() ?>/AlarmFrames() ?>
TotScore() ?>/AvgScore() ?>/MaxScore() ?>
- DiskSpace(null)) . ' on ' . $Event->Storage()->Name() ?>
+
+DiskSpace(null)) . ' on ' . $Event->Storage()->Name().
+ ( $Event->SecondaryStorageId() ? ', ' . $Event->SecondaryStorage()->Name() :'' )
+?>