From 71c8562b4620706027dc48762c5b6628b8ce8bb8 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Oct 2017 10:01:16 -0700 Subject: [PATCH 1/9] Rough in a MontageLayout class --- web/includes/MontageLayout.php | 118 +++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 web/includes/MontageLayout.php diff --git a/web/includes/MontageLayout.php b/web/includes/MontageLayout.php new file mode 100644 index 000000000..9c74e1856 --- /dev/null +++ b/web/includes/MontageLayout.php @@ -0,0 +1,118 @@ + null, + 'Name' => '', + 'Positions' => 0, +); + + public function __construct( $IdOrRow = NULL ) { + if ( $IdOrRow ) { + $row = NULL; + if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) { + $row = dbFetchOne( 'SELECT * FROM MontageLayouts WHERE Id=?', NULL, array( $IdOrRow ) ); + if ( ! $row ) { + Error("Unable to load MontageLayout record for Id=" . $IdOrRow ); + } + } elseif ( is_array( $IdOrRow ) ) { + $row = $IdOrRow; + } else { + Error("Unknown argument passed to MontageLayout Constructor ($IdOrRow)"); + return; + } + + if ( $row ) { + foreach ($row as $k => $v) { + $this->{$k} = $v; + } + } else { + Error('No row for MontageLayout ' . $IdOrRow ); + } + } # end if isset($IdOrRow) + } // 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 ) ) { + return $this->defaults{$fn}; + } else { + $backTrace = debug_backtrace(); + $file = $backTrace[1]['file']; + $line = $backTrace[1]['line']; + Warning( "Unknown function call MontageLayout->$fn from $file:$line" ); + } + } + } + + public function set( $data ) { + foreach ($data as $k => $v) { + if ( is_array( $v ) ) { + # perhaps should turn into a comma-separated string + $this->{$k} = implode(',',$v); + } else if ( is_string( $v ) ) { + $this->{$k} = trim( $v ); + } else if ( is_integer( $v ) ) { + $this->{$k} = $v; + } else if ( is_bool( $v ) ) { + $this->{$k} = $v; + } else { + Error( "Unknown type $k => $v of var " . gettype( $v ) ); + $this->{$k} = $v; + } + } + } + public static function find_all( $parameters = null, $options = null ) { + $filters = array(); + $sql = 'SELECT * FROM MontageLayouts '; + $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 and isset($options['order']) ) { + $sql .= ' ORDER BY ' . $options['order']; + } + $result = dbQuery($sql, $values); + $results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'MontageLayout'); + foreach ( $results as $row => $obj ) { + $filters[] = $obj; + } + return $filters; + } + public function save( $new_values = null ) { + if ( $new_values ) { + foreach ( $new_values as $k=>$v ) { + $this->{$k} = $v; + } + } + + $sql = 'UPDATE MontageLayouts SET '.implode(', ', array_map( function($field) {return $field.'=?';}, array_keys( $this->defaults ) ) ) . ' WHERE Id=?'; + $values = array_map( function($field){return $this->{$field};}, $this->fields ); + $values[] = $this->{'Id'}; + dbQuery( $sql, $values ); + } // end function save + +} // end class MontageLayout +?> From 9942b886f22aec4e441824ffafb37422c82edc6b Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Oct 2017 12:04:42 -0700 Subject: [PATCH 2/9] conditionally add the standard layouts --- db/zm_update-1.31.12.sql | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/db/zm_update-1.31.12.sql b/db/zm_update-1.31.12.sql index a267728f5..deb97c777 100644 --- a/db/zm_update-1.31.12.sql +++ b/db/zm_update-1.31.12.sql @@ -21,3 +21,44 @@ SET @s = (SELECT IF( PREPARE stmt FROM @s; EXECUTE stmt; + +SET @s = ( SELECT IF( + (SELECT COUNT(*) FROM MontageLayouts WHERE Name='Freeform') > 0, + "SELECT 'Freeform already in layouts'", +"INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('Freeform', '{ "default":{"float":"left"} }' );" +) ); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = ( SELECT IF( + (SELECT COUNT(*) FROM MontageLayouts WHERE Name='2 Wide') > 0, + "SELECT '2 Wide already in layouts'", +"INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('2 Wide', '{ "default":{"float":"left", "width":"49%"} }' );"; +) ); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = ( SELECT IF( + (SELECT COUNT(*) FROM MontageLayouts WHERE Name='3 Wide') > 0, + "SELECT '3 Wide already in layouts'", +"INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('3 Wide', '{ "default":{"float":"left", "width":"33%"} }' );" +) ); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = ( SELECT IF( + (SELECT COUNT(*) FROM MontageLayouts WHERE Name='4 Wide') > 0, + "SELECT '4 Wide already in layouts'", +"INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('4 Wide', '{ "default":{"float":"left", "width":"24.5%"} }' );" +) ); + +PREPARE stmt FROM @s; +EXECUTE stmt; +SET @s = ( SELECT IF( + (SELECT COUNT(*) FROM MontageLayouts WHERE Name='5 Wide') > 0, + "SELECT '5 Wide already in layouts'", + "INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('5 Wide', '{ "default":{"float":"left", "width":"19%"} }' );" +) ); + +PREPARE stmt FROM @s; +EXECUTE stmt; From b3d420b13bf2e9034162f72ba79277acaad7f0dd Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Oct 2017 12:06:07 -0700 Subject: [PATCH 3/9] add standard layouts to db creation --- db/zm_create.sql.in | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 2cff3c576..b422d2936 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -770,6 +770,12 @@ CREATE TABLE MontageLayouts ( PRIMARY KEY (`Id`) ); +INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('Freeform', '{ "default":{"float":"left"} }' ); +INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('2 Wide', '{ "default":{"float":"left", "width":"49%"} }' ); +INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('3 Wide', '{ "default":{"float":"left", "width":"33%"} }' ); +INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('4 Wide', '{ "default":{"float":"left", "width":"24.5%"} }' ); +INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('5 Wide', '{ "default":{"float":"left", "width":"19%"} }' ); + -- -- Apply the initial configuration -- From 71222d7f2f057cbd10faf76754884325a87667f6 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Oct 2017 12:06:26 -0700 Subject: [PATCH 4/9] rework how montage layouts are done, pull from db now --- web/includes/MontageLayout.php | 22 +++++---- web/skins/classic/views/js/montage.js | 57 +++++++++++++++++++---- web/skins/classic/views/js/montage.js.php | 19 ++++++-- web/skins/classic/views/montage.php | 22 +++++++-- 4 files changed, 95 insertions(+), 25 deletions(-) diff --git a/web/includes/MontageLayout.php b/web/includes/MontageLayout.php index 9c74e1856..fd105a8cd 100644 --- a/web/includes/MontageLayout.php +++ b/web/includes/MontageLayout.php @@ -1,12 +1,14 @@ null, - 'Name' => '', - 'Positions' => 0, -); + private $defaults = array( + 'Id' => null, + 'Name' => '', + 'Positions' => 0, + ); public function __construct( $IdOrRow = NULL ) { if ( $IdOrRow ) { @@ -68,7 +70,7 @@ private $defaults = array( } } } - public static function find_all( $parameters = null, $options = null ) { + public static function find( $parameters = null, $options = null ) { $filters = array(); $sql = 'SELECT * FROM MontageLayouts '; $values = array(); @@ -95,9 +97,11 @@ private $defaults = array( $sql .= ' ORDER BY ' . $options['order']; } $result = dbQuery($sql, $values); - $results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'MontageLayout'); - foreach ( $results as $row => $obj ) { - $filters[] = $obj; + if ( $result ) { + $results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'MontageLayout'); + foreach ( $results as $row => $obj ) { + $filters[] = $obj; + } } return $filters; } diff --git a/web/skins/classic/views/js/montage.js b/web/skins/classic/views/js/montage.js index 3fe8c5b87..2a4badccc 100644 --- a/web/skins/classic/views/js/montage.js +++ b/web/skins/classic/views/js/montage.js @@ -10,8 +10,6 @@ function Monitor( monitorData ) { this.streamCmdParms = "view=request&request=stream&connkey="+this.connKey; if ( auth_hash ) this.streamCmdParms += '&auth='+auth_hash; - else - console.log("No auth_hash"); this.streamCmdTimer = null; this.start = function( delay ) { @@ -112,13 +110,45 @@ function Monitor( monitorData ) { } function selectLayout( element ) { - layout = $(element).get('value') - var cssFile = skinPath+'/css/'+Cookie.read('zmCSS')+'/views/'+layout; - if ( $('dynamicStyles') ) - $('dynamicStyles').destroy(); - new Asset.css( cssFile, { id: 'dynamicStyles' } ); + layout = $(element).get('value'); + + if ( layout_id = parseInt(layout) ) { + layout = layouts[layout]; +console.log("Have layout # " + layout_id); + + for ( var i = 0; i < monitors.length; i++ ) { + monitor = monitors[i]; + // Need to clear the current positioning, and apply the new + + monitor_frame = $j('#monitorFrame'+monitor.id); + if ( ! monitor_frame ) { + console.log("Error finding frame for " + monitor.id ); + continue; + } + + // Apply default layout options, like float left + if ( layout.default ) { + styles = layout.default; + for ( style in styles ) { +console.log("applying " + style + ': ' + styles[style]); + monitor_frame.css(style, styles[style]); + } + } // end if default styles + + if ( layout[monitor.id] ) { + styles = layout[monitor.id]; + for ( style in styles ) { +console.log("applying " + style + ': ' + styles[style]); + monitor_frame.css(style, styles[style]); + } + } // end if specific monitor style + } // end foreach monitor + } // end if a stored layout + if ( ! layout ) { + return; + } Cookie.write( 'zmMontageLayout', layout, { duration: 10*365 } ); - if ( layout != 'montage_freeform.css' ) { + if ( layout_id != 1 ) { // 'montage_freeform.css' ) { Cookie.write( 'zmMontageScale', '', { duration: 10*365 } ); $('scale').set('value', '' ); $('width').set('value', ''); @@ -152,6 +182,17 @@ function changeSize() { for ( var x = 0; x < monitors.length; x++ ) { var monitor = monitors[x]; + + // Scale the frame + monitor_frame = $j('#monitorFrame'+monitor.id); + if ( ! monitor_frame ) { + console.log("Error finding frame for " + monitor.id ); + continue; + } + if ( width ) + monitor_frame.css('width',width+'px'); + if ( height ) + monitor_frame.css('height',height+'px'); /*Stream could be an applet so can't use moo tools*/ var streamImg = $( 'liveStream'+monitor.id ); if ( streamImg ) { diff --git a/web/skins/classic/views/js/montage.js.php b/web/skins/classic/views/js/montage.js.php index cd065a355..7aba1e4fb 100644 --- a/web/skins/classic/views/js/montage.js.php +++ b/web/skins/classic/views/js/montage.js.php @@ -31,12 +31,21 @@ var monitorData = new Array(); foreach ( $monitors as $monitor ) { ?> monitorData[monitorData.length] = { - 'id': Id() ?>, - 'connKey': connKey() ?>, - 'width': Width() ?>, - 'height':Height() ?>, + 'id': Id() ?>, + 'connKey': connKey() ?>, + 'width': Width() ?>, + 'height':Height() ?>, 'server_url': 'Server()->Url().(ZM_MIN_STREAMING_PORT?':'.(ZM_MIN_STREAMING_PORT+$monitor->Id()):'').$_SERVER['PHP_SELF'] ?>' }; +layouts = new Array(); +layouts[0] = {}; // reserved, should hold which fields to clear when transitioning + +layouts[Id() ?>] = Positions() ?>; + diff --git a/web/skins/classic/views/montage.php b/web/skins/classic/views/montage.php index e4cc59dde..fa5352563 100644 --- a/web/skins/classic/views/montage.php +++ b/web/skins/classic/views/montage.php @@ -23,6 +23,8 @@ if ( !canView('Stream') ) { return; } +require_once('includes/MontageLayout.php'); + $showControl = false; $showZones = false; if ( isset( $_REQUEST['showZones'] ) ) { @@ -59,6 +61,7 @@ if ( ! $scale ) $focusWindow = true; +/* $layouts = array( 'montage_freeform.css' => translate('MtgDefault'), 'montage_2wide.css' => translate('Mtg2widgrd'), @@ -66,6 +69,15 @@ $layouts = array( 'montage_4wide.css' => translate('Mtg4widgrd'), 'montage_3wide50enlarge.css' => translate('Mtg3widgrx'), ); +foreach ( MontageLayout::find() as $Layout ) { + $layouts[$Layout->Id()] = $Layout->Name(); +} +*/ +$layouts = MontageLayout::find(NULL, array('order'=>"lower('Name')")); +$layoutsById = array(); +foreach ( $layouts as $l ) { + $layoutsById[$l->Id()] = $l->Name(); +} $layout = ''; if ( isset($_COOKIE['zmMontageLayout']) ) @@ -141,11 +153,11 @@ if ( $showControl ) { } if ( $showZones ) { ?> - Hide Zones + Hide Zones - Show Zones + Show Zones @@ -157,10 +169,14 @@ if ( $showZones ) { 'changeMonitor(this);') ); ?> +
- + + + +
From eb48759ff8d193b0226e59f38b8187a825623643 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Oct 2017 13:37:51 -0700 Subject: [PATCH 5/9] wip --- src/zm_monitorstream.cpp | 2 +- web/ajax/stream.php | 2 +- web/skins/classic/views/js/montage.js | 44 +++++++++++++- web/skins/classic/views/montage.php | 87 +++++++++++++++++++++++++-- 4 files changed, 126 insertions(+), 9 deletions(-) diff --git a/src/zm_monitorstream.cpp b/src/zm_monitorstream.cpp index f904f23cf..85b223f6a 100644 --- a/src/zm_monitorstream.cpp +++ b/src/zm_monitorstream.cpp @@ -553,7 +553,7 @@ void MonitorStream::runStream() { Debug( 2, "Assigned temporary buffer" ); } } - } + } // end if connkey & playback_buffer float max_secs_since_last_sent_frame = 10.0; //should be > keep alive amount (5 secs) while ( !zm_terminate ) { diff --git a/web/ajax/stream.php b/web/ajax/stream.php index 7f050bda8..2d4c3c612 100644 --- a/web/ajax/stream.php +++ b/web/ajax/stream.php @@ -44,7 +44,7 @@ switch ( $_REQUEST['command'] ) { $remSockFile = ZM_PATH_SOCKS.'/zms-'.sprintf("%06d",$_REQUEST['connkey']).'s.sock'; $max_socket_tries = 10; while ( !file_exists($remSockFile) && $max_socket_tries-- ) { //sometimes we are too fast for our own good, if it hasn't been setup yet give it a second. - usleep(200000); + usleep(2000000); } if ( !file_exists($remSockFile) ) { diff --git a/web/skins/classic/views/js/montage.js b/web/skins/classic/views/js/montage.js index 2a4badccc..c2f96258c 100644 --- a/web/skins/classic/views/js/montage.js +++ b/web/skins/classic/views/js/montage.js @@ -87,8 +87,12 @@ function Monitor( monitorData ) { } else { console.error( respObj.message ); // Try to reload the image stream. - if ( stream ) + if ( stream ) { + console.log('Reloading stream: ' + stream.src ); stream.src = stream.src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) )); + } else { + console.log( 'No stream to reload?' ); + } } var streamCmdTimeout = statusRefreshTimeout; if ( this.alarmState == STATE_ALARM || this.alarmState == STATE_ALERT ) @@ -147,7 +151,7 @@ console.log("applying " + style + ': ' + styles[style]); if ( ! layout ) { return; } - Cookie.write( 'zmMontageLayout', layout, { duration: 10*365 } ); + Cookie.write( 'zmMontageLayout', layout_id, { duration: 10*365 } ); if ( layout_id != 1 ) { // 'montage_freeform.css' ) { Cookie.write( 'zmMontageScale', '', { duration: 10*365 } ); $('scale').set('value', '' ); @@ -264,6 +268,42 @@ function initPage() { monitors[i].start( delay ); } selectLayout($('layout')); + + $j('#monitors .monitorFrame').draggable({ + cursor: 'crosshair', + revert: 'invalid' + }); + + function toGrid(value) { + return Math.round(value / 80) * 80; + } + + $j('#monitors').droppable({ + accept: '#monitors .monitorFrame', + drop: function(event, ui) { + //console.log(event); + $j(this).removeClass('border over'); + $j(ui.draggable).detach(). + appendTo($j(this).find('ul')). + draggable({ + containment: '.fw-content', + cursor: 'help', + grid: [ 80, 80 ] + }). + css({ + position: 'absolute', + left: toGrid(event.clientX - $j('#monitors').offset().left), + top: toGrid(event.clientY - $j('#monitors').offset().top) + }); + }, + over: function(event, elem) { + console.log('over'); + $j(this).addClass('over'); + }, + out: function(event, elem) { + $j(this).removeClass('over'); + } + }); } // Kick everything off diff --git a/web/skins/classic/views/montage.php b/web/skins/classic/views/montage.php index fa5352563..fe9d688e0 100644 --- a/web/skins/classic/views/montage.php +++ b/web/skins/classic/views/montage.php @@ -103,6 +103,35 @@ ob_end_clean(); $groupSql = Group::get_group_sql( $group_id ); +$servers = Server::find_all(); +$ServersById = array(); +foreach ( $servers as $S ) { + $ServersById[$S->Id()] = $S; +} +session_start(); +foreach ( array('ServerFilter','StorageFilter') as $var ) { + if ( isset( $_REQUEST[$var] ) ) { + if ( $_REQUEST[$var] != '' ) { + $_SESSION[$var] = $_REQUEST[$var]; + } else { + unset( $_SESSION[$var] ); + } + } else if ( isset( $_COOKIE[$var] ) ) { + if ( $_COOKIE[$var] != '' ) { + $_SESSION[$var] = $_COOKIE[$var]; + } else { + unset($_SESSION[$var]); + } + } +} +session_write_close(); + +$storage_areas = Storage::find_all(); +$StorageById = array(); +foreach ( $storage_areas as $S ) { + $StorageById[$S->Id()] = $S; +} + $monitor_id = 0; if ( isset( $_REQUEST['monitor_id'] ) ) { $monitor_id = $_REQUEST['monitor_id']; @@ -112,15 +141,46 @@ if ( isset( $_REQUEST['monitor_id'] ) ) { $monitors = array(); $monitors_dropdown = array( '' => 'All' ); -$sql = "SELECT * FROM Monitors WHERE Function != 'None'"; -if ( $groupSql ) { $sql .= ' AND ' . $groupSql; }; -if ( $monitor_id ) { $sql .= ' AND Id='.$monitor_id; }; + $conditions = array(); + $values = array(); -$sql .= ' ORDER BY Sequence'; -foreach( dbFetchAll( $sql ) as $row ) { + if ( $groupSql ) + $conditions[] = $groupSql; + if ( isset($_SESSION['ServerFilter']) ) { + $conditions[] = 'ServerId=?'; + $values[] = $_SESSION['ServerFilter']; + } + if ( isset($_SESSION['StorageFilter']) ) { + $conditions[] = 'StorageId=?'; + $values[] = $_SESSION['StorageFilter']; + } + $sql = 'SELECT * FROM Monitors' . ( count($conditions) ? ' WHERE ' . implode(' AND ', $conditions ) : '' ).' ORDER BY Sequence ASC'; + $monitor_rows = dbFetchAll( $sql, null, $values ); + + if ( $monitor_id ) { + $found_selected_monitor = false; + + for ( $i = 0; $i < count($monitor_rows); $i++ ) { + if ( !visibleMonitor( $monitor_rows[$i]['Id'] ) ) { + continue; + } + $monitors_dropdown[$monitor_rows[$i]['Id']] = $monitor_rows[$i]['Name']; + if ( $monitor_rows[$i]['Id'] == $monitor_id ) { + $found_selected_monitor = true; + } + } + if ( ! $found_selected_monitor ) { + $monitor_id = ''; + } + } + +$monitors = array(); +foreach( $monitor_rows as $row ) { if ( !visibleMonitor( $row['Id'] ) ) { continue; } + if ( $monitor_id and $row['Id'] != $monitor_id ) + continue; $row['Scale'] = $scale; $row['PopupScale'] = reScale( SCALE_BASE, $row['DefaultScale'], ZM_WEB_DEFAULT_SCALE ); @@ -169,6 +229,23 @@ if ( $showZones ) { 'changeMonitor(this);') ); ?> + 0 ) { ?> + +'All')+$ServersById, (isset($_SESSION['ServerFilter'])?$_SESSION['ServerFilter']:''), array('onchange'=>'changeFilter(this);') ); +?> + + 0 ) { ?> + +'All')+$StorageById, (isset($_SESSION['StorageFilter'])?$_SESSION['StorageFilter']:''), array('onchange'=>'changeFilter(this);') ); +?> + +
From 8b4468caeda80c5661fbf31ac96415770786391d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Oct 2017 09:05:07 -0700 Subject: [PATCH 7/9] add apache log style aliases --- scripts/ZoneMinder/lib/ZoneMinder/Logger.pm | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm b/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm index 031a2b7b6..d0d6b234b 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm @@ -678,6 +678,8 @@ sub Dump { fetch()->logPrint( DEBUG, Data::Dumper->Dump( [ $var ], [ $label ] ) ); } +sub debug { fetch()->logPrint( DEBUG, @_ ); } + sub Debug( @ ) { fetch()->logPrint( DEBUG, @_ ); } @@ -685,14 +687,24 @@ sub Debug( @ ) { sub Info( @ ) { fetch()->logPrint( INFO, @_ ); } +sub info { + fetch()->logPrint( INFO, @_ ); +} + sub Warning( @ ) { fetch()->logPrint( WARNING, @_ ); } +sub warn { + fetch()->logPrint( WARNING, @_ ); +} sub Error( @ ) { fetch()->logPrint( ERROR, @_ ); } +sub error { + fetch()->logPrint( ERROR, @_ ); +} sub Fatal( @ ) { fetch()->logPrint( FATAL, @_ ); From 2d8a4794de3bfaddacb3fdacc4ef5f1e11ba7d4f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Oct 2017 09:05:25 -0700 Subject: [PATCH 8/9] add save, set, transform, and some globals pointing to dbh and log --- scripts/ZoneMinder/lib/ZoneMinder/Object.pm | 269 +++++++++++++++++++- 1 file changed, 268 insertions(+), 1 deletion(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Object.pm b/scripts/ZoneMinder/lib/ZoneMinder/Object.pm index 63193bccf..af4d9fd21 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Object.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Object.pm @@ -42,7 +42,12 @@ use ZoneMinder::Config qw(:all); use ZoneMinder::Logger qw(:all); use ZoneMinder::Database qw(:all); -use vars qw/ $AUTOLOAD /; +use vars qw/ $AUTOLOAD $log $dbh/; + +*log = \$ZoneMinder::Logger::logger; +*dbh = \$ZoneMinder::Database::dbh; + +my $debug = 1; sub new { my ( $parent, $id, $data ) = @_; @@ -110,7 +115,269 @@ sub AUTOLOAD { return $_[0]{$name}; } +sub save { + my ( $self, $data, $force_insert ) = @_; + my $type = ref $self; + if ( ! $type ) { + my ( $caller, undef, $line ) = caller; + $log->error("No type in Object::save. self:$self from $caller:$line"); + } + my $local_dbh = eval '$'.$type.'::dbh'; + $local_dbh = $ZoneMinder::Database::dbh if ! $local_dbh; + $self->set( $data ? $data : {} ); + if ( $debug or DEBUG_ALL ) { + if ( $data ) { + foreach my $k ( keys %$data ) { + $log->debug("Object::save after set $k => $$data{$k} $$self{$k}"); + } + } else { + $log->debug("No data after set"); + } + } +#$debug = 0; + + my $table = eval '$'.$type.'::table'; + my $fields = eval '\%'.$type.'::fields'; + my $debug = eval '$'.$type.'::debug'; + #$debug = DEBUG_ALL if ! $debug; + + my %sql; + foreach my $k ( keys %$fields ) { + $sql{$$fields{$k}} = $$self{$k} if defined $$fields{$k}; + } # end foreach + if ( ! $force_insert ) { + $sql{$$fields{updated_on}} = 'NOW()' if exists $$fields{updated_on}; + } # end if + my $serial = eval '$'.$type.'::serial'; + my @identified_by = eval '@'.$type.'::identified_by'; + + my $ac = sql::start_transaction( $local_dbh ); + if ( ! $serial ) { + my $insert = $force_insert; + my %serial = eval '%'.$type.'::serial'; + if ( ! %serial ) { +$log->debug("No serial") if $debug; + # No serial columns defined, which means that we will do saving by delete/insert instead of insert/update + if ( @identified_by ) { + my $where = join(' AND ', map { $$fields{$_}.'=?' } @identified_by ); + if ( $debug ) { + $log->debug("DELETE FROM $table WHERE $where"); + } # end if + + if ( ! ( ( $_ = $local_dbh->prepare("DELETE FROM $table WHERE $where") ) and $_->execute( @$self{@identified_by} ) ) ) { + $where =~ s/\?/\%s/g; + $log->error("Error deleting: DELETE FROM $table WHERE " . sprintf($where, map { defined $_ ? $_ : 'undef' } ( @$self{@identified_by}) ).'):' . $local_dbh->errstr); + $local_dbh->rollback(); + sql::end_transaction( $local_dbh, $ac ); + return $local_dbh->errstr; + } elsif ( $debug ) { + $log->debug("SQL succesful DELETE FROM $table WHERE $where"); + } # end if + } # end if + $insert = 1; + } else { + foreach my $id ( @identified_by ) { + if ( ! $serial{$id} ) { + my ( $caller, undef, $line ) = caller; + $log->error("$id nor in serial for $type from $caller:$line") if $debug; + next; + } + if ( ! $$self{$id} ) { + ($$self{$id}) = ($sql{$$fields{$id}}) = $local_dbh->selectrow_array( q{SELECT nextval('} . $serial{$id} . q{')} ); + $log->debug("SQL statement execution SELECT nextval('$serial{$id}') returned $$self{$id}") if $debug or DEBUG_ALL; + $insert = 1; + } # end if + } # end foreach + } # end if ! %serial + + if ( $insert ) { + my @keys = keys %sql; + my $command = "INSERT INTO $table (" . join(',', @keys ) . ') VALUES (' . join(',', map { '?' } @sql{@keys} ) . ')'; + if ( ! ( ( $_ = $local_dbh->prepare($command) ) and $_->execute( @sql{@keys} ) ) ) { + my $error = $local_dbh->errstr; + $command =~ s/\?/\%s/g; + $log->error('SQL statement execution failed: ('.sprintf($command, , map { defined $_ ? $_ : 'undef' } ( @sql{@keys}) ).'):' . $local_dbh->errstr); + $local_dbh->rollback(); + sql::end_transaction( $local_dbh, $ac ); + return $error; + } # end if + if ( $debug or DEBUG_ALL ) { + $command =~ s/\?/\%s/g; + $log->debug('SQL statement execution: ('.sprintf($command, , map { defined $_ ? $_ : 'undef' } ( @sql{@keys} ) ).'):' ); + } # end if + } else { + my @keys = keys %sql; + my $command = "UPDATE $table SET " . join(',', map { $_ . ' = ?' } @keys ) . ' WHERE ' . join(' AND ', map { $_ . ' = ?' } @$fields{@identified_by} ); + if ( ! ( $_ = $local_dbh->prepare($command) and $_->execute( @sql{@keys,@$fields{@identified_by}} ) ) ) { + my $error = $local_dbh->errstr; + $command =~ s/\?/\%s/g; + $log->error('SQL failed: ('.sprintf($command, , map { defined $_ ? $_ : 'undef' } ( @sql{@keys, @$fields{@identified_by}}) ).'):' . $local_dbh->errstr); + $local_dbh->rollback(); + sql::end_transaction( $local_dbh, $ac ); + return $error; + } # end if + if ( $debug or DEBUG_ALL ) { + $command =~ s/\?/\%s/g; + $log->debug('SQL DEBUG: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys,@$fields{@identified_by}} ) ).'):' ); + } # end if + } # end if + } else { # not identified_by + @identified_by = ('id') if ! @identified_by; + my $need_serial = ! ( @identified_by == map { $$self{$_} ? $_ : () } @identified_by ); + + if ( $force_insert or $need_serial ) { + + if ( $need_serial ) { + if ( $serial ) { + @$self{@identified_by} = @sql{@$fields{@identified_by}} = $local_dbh->selectrow_array( q{SELECT nextval('} . $serial . q{')} ); + if ( $local_dbh->errstr() ) { + $log->error("Error getting next id. " . $local_dbh->errstr() ); + $log->error("SQL statement execution SELECT nextval('$serial') returned ".join(',',@$self{@identified_by})); + } elsif ( $debug or DEBUG_ALL ) { + $log->debug("SQL statement execution SELECT nextval('$serial') returned ".join(',',@$self{@identified_by})); + } # end if + } # end if + } # end if + my @keys = keys %sql; + my $command = "INSERT INTO $table (" . join(',', @keys ) . ') VALUES (' . join(',', map { '?' } @sql{@keys} ) . ')'; + if ( ! ( $_ = $local_dbh->prepare($command) and $_->execute( @sql{@keys} ) ) ) { + $command =~ s/\?/\%s/g; + my $error = $local_dbh->errstr; + $log->error('SQL failed: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys}) ).'):' . $error); + $local_dbh->rollback(); + sql::end_transaction( $local_dbh, $ac ); + return $error; + } # end if + if ( $debug or DEBUG_ALL ) { + $command =~ s/\?/\%s/g; + $log->debug('SQL DEBUG: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys} ) ).'):' ); + } # end if + } else { + delete $sql{created_on}; + my @keys = keys %sql; + @keys = sets::exclude( [ @$fields{@identified_by} ], \@keys ); + my $command = "UPDATE $table SET " . join(',', map { $_ . ' = ?' } @keys ) . ' WHERE ' . join(' AND ', map { $$fields{$_} .'= ?' } @identified_by ); + if ( ! ( $_ = $local_dbh->prepare($command) and $_->execute( @sql{@keys}, @sql{@$fields{@identified_by}} ) ) ) { + my $error = $local_dbh->errstr; + $command =~ s/\?/\%s/g; + $log->error('SQL failed: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys}, @sql{@$fields{@identified_by}} ) ).'):' . $error) if $log; + $local_dbh->rollback(); + sql::end_transaction( $local_dbh, $ac ); + return $error; + } # end if + if ( $debug or DEBUG_ALL ) { + $command =~ s/\?/\%s/g; + $log->debug('SQL DEBUG: ('.sprintf($command, map { defined $_ ? ( ref $_ eq 'ARRAY' ? join(',',@{$_}) : $_ ) : 'undef' } ( @sql{@keys}, @$self{@identified_by} ) ).'):' ); + } # end if + } # end if + } # end if + sql::end_transaction( $local_dbh, $ac ); + $self->load(); + #if ( $$fields{id} ) { + #if ( ! $ZoneMinder::Object::cache{$type}{$$self{id}} ) { + #$ZoneMinder::Object::cache{$type}{$$self{id}} = $self; + #} # end if + #delete $ZoneMinder::Object::cache{$config{db_name}}{$type}{$$self{id}}; + #} # end if +#$log->debug("after delete"); + #eval 'if ( %'.$type.'::find_cache ) { %'.$type.'::find_cache = (); }'; +#$log->debug("after clear cache"); + return ''; +} # end sub save + +sub set { + my ( $self, $params ) = @_; + my @set_fields = (); + + my $type = ref $self; + my %fields = eval ('%'.$type.'::fields'); + if ( ! %fields ) { + $log->warn('ZoneMinder::Object::set called on an object with no fields'); + } # end if + my %defaults = eval('%'.$type.'::defaults'); + if ( ref $params ne 'HASH' ) { + my ( $caller, undef, $line ) = caller; + $openprint::log->error("$type -> set called with non-hash params from $caller $line"); + } + + foreach my $field ( keys %fields ) { +$log->debug("field: $field, param: ".$$params{$field}) if $debug; + if ( exists $$params{$field} ) { +$openprint::log->debug("field: $field, $$self{$field} =? param: ".$$params{$field}) if $debug; + if ( ( ! defined $$self{$field} ) or ($$self{$field} ne $params->{$field}) ) { +# Only make changes to fields that have changed + if ( defined $fields{$field} ) { + $$self{$field} = $$params{$field} if defined $fields{$field}; + push @set_fields, $fields{$field}, $$params{$field}; #mark for sql updating + } # end if +$openprint::log->debug("Running $field with $$params{$field}") if $debug; + if ( my $func = $self->can( $field ) ) { + $func->( $self, $$params{$field} ); + } # end if + } # end if + } # end if + + if ( defined $fields{$field} ) { + if ( $$self{$field} ) { + $$self{$field} = transform( $type, $field, $$self{$field} ); + } # end if $$self{field} + } + } # end foreach field + + foreach my $field ( keys %defaults ) { + + if ( ( ! exists $$self{$field} ) or (!defined $$self{$field}) or ( $$self{$field} eq '' ) ) { + $log->debug("Setting default ($field) ($$self{$field}) ($defaults{$field}) ") if $debug; + if ( defined $defaults{$field} ) { + $log->debug("Default $field is defined: $defaults{$field}") if $debug; + if ( $defaults{$field} eq 'NOW()' ) { + $$self{$field} = 'NOW()'; + } else { + $$self{$field} = eval($defaults{$field}); + $log->error( "Eval error of object default $field default ($defaults{$field}) Reason: " . $@ ) if $@; + } # end if + } else { + $$self{$field} = $defaults{$field}; + } # end if +#$$self{$field} = ( defined $defaults{$field} ) ? eval($defaults{$field}) : $defaults{$field}; + $log->debug("Setting default for ($field) using ($defaults{$field}) to ($$self{$field}) ") if $debug; + } # end if + } # end foreach default + return @set_fields; +} # end sub set + +sub transform { + my $type = ref $_[0]; + $type = $_[0] if ! $type; + my $fields = eval '\%'.$type.'::fields'; + my $value = $_[2]; + + if ( defined $$fields{$_[1]} ) { + my @transforms = eval('@{$'.$type.'::transforms{$_[1]}}'); + $openprint::log->debug("Transforms for $_[1] before $_[2]: @transforms") if $debug; + if ( @transforms ) { + foreach my $transform ( @transforms ) { + if ( $transform =~ /^s\// or $transform =~ /^tr\// ) { + eval '$value =~ ' . $transform; + } elsif ( $transform =~ /^<(\d+)/ ) { + if ( $value > $1 ) { + $value = undef; + } # end if + } else { + $openprint::log->debug("evalling $value ".$transform . " Now value is $value" ); + eval '$value '.$transform; + $openprint::log->error("Eval error $@") if $@; + } + $openprint::log->debug("After $transform: $value") if $debug; + } # end foreach + } # end if + } else { + $openprint::log->error("Object::transform ($_[1]) not in fields for $type"); + } # end if + return $value; + +} # end sub transform 1; __END__ From 2d6998ad2577dcd2cd79bd441ef8c9b4d57ca602 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 27 Oct 2017 15:21:10 -0700 Subject: [PATCH 9/9] fix debug_all --- scripts/ZoneMinder/lib/ZoneMinder/Object.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Object.pm b/scripts/ZoneMinder/lib/ZoneMinder/Object.pm index af4d9fd21..a2d9fa021 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Object.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Object.pm @@ -48,6 +48,7 @@ use vars qw/ $AUTOLOAD $log $dbh/; *dbh = \$ZoneMinder::Database::dbh; my $debug = 1; +use constant DEBUG_ALL=>0; sub new { my ( $parent, $id, $data ) = @_;