From e39a95d7618efa1f6147397f746412f94e590a79 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 18 Dec 2019 19:06:10 -0500 Subject: [PATCH 1/6] Add AlarmedZoned to filters, work on fixing filter behaviour in js. Enable viewing filter results in montagereview --- web/includes/functions.php | 13 +++++- web/index.php | 4 +- web/lang/en_gb.php | 1 + web/skins/classic/views/filter.php | 38 ++++++++++++---- web/skins/classic/views/js/filter.js | 55 ++++++++++++++++------- web/skins/classic/views/js/filter.js.php | 1 + web/skins/classic/views/js/montage.js | 1 + web/skins/classic/views/montagereview.php | 11 +++++ 8 files changed, 97 insertions(+), 27 deletions(-) diff --git a/web/includes/functions.php b/web/includes/functions.php index aea0c8b14..ca2dd6a05 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -1094,6 +1094,7 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') { for ( $i = 0; $i < count($terms); $i++ ) { $term = $terms[$i]; +ZM\Logger::Debug("Term: " . print_r($term,true)); if ( isset($term['cnj']) && array_key_exists($term['cnj'], $validQueryConjunctionTypes) ) { $filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][cnj]").'='.urlencode($term['cnj']); @@ -1109,6 +1110,12 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') { $filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][attr]").'='.urlencode($term['attr']); $filter['fields'] .= "\n"; switch ( $term['attr'] ) { + case 'AlarmedZoneId': + if ( $term['op'] != 'IN' ) { + ZM\Warning("AlarmedZoneId only supports the IN operator"); + } + $filter['sql'] .= $term['val']; + break; case 'MonitorName': $filter['sql'] .= 'M.Name'; break; @@ -1226,11 +1233,15 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') { $valueList = array(); foreach ( preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $term['val'])) as $value ) { switch ( $term['attr'] ) { + + case 'AlarmedZoneId': + $value = '(SELECT DISTINCT ZoneId FROM Stats WHERE EventId=E.Id)'; + break; case 'MonitorName': case 'Name': case 'Cause': case 'Notes': - if($term['op'] == 'LIKE' || $term['op'] == 'NOT LIKE') { + if ( $term['op'] == 'LIKE' || $term['op'] == 'NOT LIKE' ) { $value = '%'.$value.'%'; } $value = dbEscape($value); diff --git a/web/index.php b/web/index.php index 83be992e8..270c23b86 100644 --- a/web/index.php +++ b/web/index.php @@ -34,7 +34,7 @@ if ( version_compare(phpversion(), '4.1.0', '<') ) { } // Useful debugging lines for mobile devices -if ( false ) { +if ( true ) { ob_start(); phpinfo(INFO_VARIABLES); $fp = fopen('/tmp/env.html', 'w+'); @@ -71,7 +71,7 @@ define('ZM_BASE_URL', ''); require_once('includes/functions.php'); if ( $_SERVER['REQUEST_METHOD'] == 'OPTIONS' ) { - ZM\Logger::Debug("OPTIONS Method, only doing CORS"); + ZM\Logger::Debug('OPTIONS Method, only doing CORS'); # Add Cross domain access headers CORSHeaders(); return; diff --git a/web/lang/en_gb.php b/web/lang/en_gb.php index a6f2fa488..fecc57490 100644 --- a/web/lang/en_gb.php +++ b/web/lang/en_gb.php @@ -116,6 +116,7 @@ $SLANG = array( 'Area' => 'Area', 'AreaUnits' => 'Area (px/%)', 'AttrAlarmFrames' => 'Alarm Frames', + 'AttrAlarmedZone' => 'Alarmed Zone', 'AttrArchiveStatus' => 'Archive Status', 'AttrAvgScore' => 'Avg. Score', 'AttrCause' => 'Cause', diff --git a/web/skins/classic/views/filter.php b/web/skins/classic/views/filter.php index 425e914c9..be388fe0e 100644 --- a/web/skins/classic/views/filter.php +++ b/web/skins/classic/views/filter.php @@ -1,6 +1,6 @@ translate('ChooseFilter')); @@ -69,6 +71,7 @@ if ( count($terms) ) { $attrTypes = array( 'AlarmFrames' => translate('AttrAlarmFrames'), + 'AlarmedZoneId' => translate('AttrAlarmedZone'), 'Archived' => translate('AttrArchiveStatus'), 'AvgScore' => translate('AttrAvgScore'), 'Cause' => translate('AttrCause'), @@ -78,17 +81,17 @@ $attrTypes = array( 'EndDateTime' => translate('AttrEndDateTime'), 'EndDate' => translate('AttrEndDate'), 'EndTime' => translate('AttrEndTime'), + 'EndWeekday' => translate('AttrEndWeekday'), 'FilterServerId' => translate('AttrFilterServer'), 'Frames' => translate('AttrFrames'), - 'EndWeekday' => translate('AttrEndWeekday'), 'Id' => translate('AttrId'), 'Length' => translate('AttrDuration'), - 'Name' => translate('AttrName'), - 'Notes' => translate('AttrNotes'), 'MaxScore' => translate('AttrMaxScore'), 'MonitorId' => translate('AttrMonitorId'), 'MonitorName' => translate('AttrMonitorName'), 'MonitorServerId' => translate('AttrMonitorServer'), + 'Name' => translate('AttrName'), + 'Notes' => translate('AttrNotes'), 'SecondaryStorageId' => translate('AttrSecondaryStorageArea'), 'ServerId' => translate('AttrMonitorServer'), 'StartDateTime' => translate('AttrStartDateTime'), @@ -143,13 +146,22 @@ foreach ( dbFetchAll('SELECT `Id`, `Name` FROM `Servers` ORDER BY lower(`Name`) $servers[$server['Id']] = validHtmlStr($server['Name']); } $monitors = array(); +$monitor_names = array(); foreach ( dbFetchAll('SELECT `Id`, `Name` FROM `Monitors` ORDER BY lower(`Name`) ASC') as $monitor ) { if ( visibleMonitor($monitor['Id']) ) { - $monitors[$monitor['Name']] = validHtmlStr($monitor['Name']); + $monitors[$monitor['Id']] = new ZM\Monitor($monitor); + $monitor_names[] = validHtmlStr($monitor['Name']); + } +} +$zones = array(); +foreach ( dbFetchAll('SELECT Id, Name, MonitorId FROM Zones ORDER BY lower(`Name`) ASC') as $zone ) { + if ( visibleMonitor($zone['MonitorId']) ) { + $zone['Name'] = validHtmlStr($monitors[$zone['MonitorId']]->Name().': '.$zone['Name']); + $zones[$zone['Id']] = new ZM\Zone($zone); } } -xhtmlHeaders(__FILE__, translate('EventFilter') ); +xhtmlHeaders(__FILE__, translate('EventFilter')); ?>
@@ -259,10 +271,15 @@ for ( $i=0; $i < count($terms); $i++ ) { + + + @@ -273,6 +290,11 @@ for ( $i=0; $i < count($terms); $i++ ) { ?> + + + @@ -409,7 +431,7 @@ if ( ZM_OPT_MESSAGE ) {
- + 0) { //add bracket select to all rows + if ( brackets > 0 ) { //add bracket select to all rows var obrSelect = $j('').attr('name', queryPrefix + rowNum + '][obr]').attr('id', queryPrefix + rowNum + '][obr]'); var cbrSelect = $j('').attr('name', queryPrefix + rowNum + '][cbr]').attr('id', queryPrefix + rowNum + '][cbr]'); obrSelect.append(''); cbrSelect.append(''); } @@ -176,7 +178,7 @@ function parseRows(rows) { inputTds.eq(5).html(' '); } - if (rows.length == 1) { + if ( rows.length == 1 ) { inputTds.eq(6).find(':input[value="-"]').prop('disabled', true); //enable/disable remove row button } else { inputTds.eq(6).find(':input[value="-"]').prop('disabled', false); @@ -184,7 +186,7 @@ function parseRows(rows) { var attr = inputTds.eq(2).children().val(); - if ( attr == "Archived") { //Archived types + if ( attr == 'Archived' ) { //Archived types inputTds.eq(3).html('equal to'); var archiveSelect = $j('').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]'); for (var i = 0; i < archiveTypes.length; i++) { @@ -192,6 +194,18 @@ function parseRows(rows) { } var archiveVal = inputTds.eq(4).children().val(); inputTds.eq(4).html(archiveSelect).children().val(archiveVal).chosen({width: "101%"}); + } else if ( attr == 'AlarmedZoneId' ) { + var zoneSelect = $j('').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]'); + for ( monitor_id in monitors ) { + for ( zone_id in zones ) { + var zone = zones[zone_id]; + if ( monitor_id == zone.MonitorId ) { + zoneSelect.append(''); + } + } // end foreach zone + } // end foreach monitor + var zoneVal = inputTds.eq(4).children().val(); + inputTds.eq(4).html(zoneSelect).children().val(zoneVal).chosen({width: "101%"}); } else if ( attr.indexOf('Weekday') >= 0 ) { //Weekday selection var weekdaySelect = $j('').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]'); for (var i = 0; i < weekdays.length; i++) { @@ -222,18 +236,27 @@ function parseRows(rows) { inputTds.eq(4).html(storageSelect).children().val(storageVal).chosen({width: "101%"}); } else if ( attr == 'MonitorName' ) { //Monitor names var monitorSelect = $j('').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]'); - for (var key in monitors) { - monitorSelect.append(''); + for ( var monitor_id in monitors ) { + monitorSelect.append(''); } var monitorVal = inputTds.eq(4).children().val(); inputTds.eq(4).html(monitorSelect).children().val(monitorVal); - } else { //Reset to regular text field and operator for everything that isn't special + } else { // Reset to regular text field and operator for everything that isn't special +if ( 0 ) { + // We don't actually do anything different in terms of operators. So why do it for text? var opSelect = $j('').attr('name', queryPrefix + rowNum + '][op]').attr('id', queryPrefix + rowNum + '][op]'); - for (var key in opTypes) { - opSelect.append(''); - } var opVal = inputTds.eq(3).children().val(); - inputTds.eq(3).html(opSelect).children().val(opVal).chosen({width: "101%"}); + if ( ! opVal ) { + // Default to equals so that something gets selected + console.log("No value for operator. Defaulting to ="); + opVal = '='; + } + for ( var key in opTypes ) { + opSelect.append(''); + } + inputTds.eq(3).html(opSelect).children().val(opVal).chosen({width: "101%"}); +} + var textInput = $j('').attr('type', 'text').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]'); var textVal = inputTds.eq(4).children().val(); inputTds.eq(4).html(textInput).children().val(textVal); @@ -253,7 +276,7 @@ function parseRows(rows) { term[2] = rowNum; inputTds.eq(2).children().eq(0).attr('name', 'filter'+stringFilter(term)); inputTds.eq(2).children().eq(0).attr('id', 'filter'+stringFilter(term)); - }//End for each term/row + } // end foreach term/row history.replaceState(null, null, '?view=filter&' + $j('#contentForm').serialize()); } diff --git a/web/skins/classic/views/js/filter.js.php b/web/skins/classic/views/js/filter.js.php index 15db50a32..31cd939a0 100644 --- a/web/skins/classic/views/js/filter.js.php +++ b/web/skins/classic/views/js/filter.js.php @@ -10,6 +10,7 @@ var states = ; var servers = ; var storageareas = ; var monitors = ; +var zones = ; var errorBrackets = ''; var errorValue = ''; diff --git a/web/skins/classic/views/js/montage.js b/web/skins/classic/views/js/montage.js index 00130f108..3a491941b 100644 --- a/web/skins/classic/views/js/montage.js +++ b/web/skins/classic/views/js/montage.js @@ -464,6 +464,7 @@ function initPage() { // Start the fps and status updates. give a random delay so that we don't assault the server var delay = Math.round( (Math.random()+0.5)*statusRefreshTimeout ); +console.log("delay: " + delay); monitors[i].start(delay); var interval = monitors[i].refresh; diff --git a/web/skins/classic/views/montagereview.php b/web/skins/classic/views/montagereview.php index 4e216f380..0d3571523 100644 --- a/web/skins/classic/views/montagereview.php +++ b/web/skins/classic/views/montagereview.php @@ -62,6 +62,17 @@ ob_end_clean(); $filter = array(); if ( isset($_REQUEST['filter']) ) { $filter = $_REQUEST['filter']; + + # Try to guess min/max time from filter + foreach ( $filter['Query'] as $term ) { + if ( $term['attr'] == 'StartDateTime' ) { + if ( $term['op'] == '<=' or $term['op'] == '<' ) { + $maxTime = $term['val']; + } else if ( $term['op'] == '>=' or $term['op'] == '>' ) { + $minTime = $term['val']; + } + } + } } else { if ( isset($_REQUEST['minTime']) && isset($_REQUEST['maxTime']) && (count($displayMonitors) != 0) ) { From adf376e4a66e83d01b7e198ab1edd3dd62bcd21c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 24 Jan 2020 09:45:02 -0500 Subject: [PATCH 2/6] add Zone.php --- web/includes/Zone.php | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 web/includes/Zone.php diff --git a/web/includes/Zone.php b/web/includes/Zone.php new file mode 100644 index 000000000..7a19df9d2 --- /dev/null +++ b/web/includes/Zone.php @@ -0,0 +1,41 @@ + null, + 'Name' => '', + 'Type' => 'Active', + 'Units' => 'Pixels', + 'CheckMethod' => 'Blobs', + 'MinPixelThreshold' => null, + 'MaxPixelThreshold' => null, + 'MinAlarmPixels' => null, + 'MaxAlarmPixels' => null, + 'FilterX' => null, + 'FilterY' => null, + 'MinFilterPixels' => null, + 'MaxFilterPixels' => null, + 'MinBlobPixels' => null, + 'MaxBlobPixels' => null, + 'MinBlobs' => null, + 'MaxBlobs' => null, + 'OverloadFrames' => 0, + 'ExtendAlarmFrames' => 0, + ); + + 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); + } + +} # end class Zone +?> From df5bf788d9e4c66fb46b2048f37a55f095d1b60a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 24 Jan 2020 11:09:27 -0500 Subject: [PATCH 3/6] Filtering by Alarmed Zone now only supports a single value using EXISTS as the operator. We now also support CURDATE() and NOW() as values for Date/StartDate/EndDate --- web/includes/functions.php | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/web/includes/functions.php b/web/includes/functions.php index ede3d53a0..19567a5c1 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -1111,10 +1111,7 @@ ZM\Logger::Debug("Term: " . print_r($term,true)); $filter['fields'] .= "\n"; switch ( $term['attr'] ) { case 'AlarmedZoneId': - if ( $term['op'] != 'IN' ) { - ZM\Warning("AlarmedZoneId only supports the IN operator"); - } - $filter['sql'] .= $term['val']; + $term['op'] = 'EXISTS'; break; case 'MonitorName': $filter['sql'] .= 'M.Name'; @@ -1235,7 +1232,7 @@ ZM\Logger::Debug("Term: " . print_r($term,true)); switch ( $term['attr'] ) { case 'AlarmedZoneId': - $value = '(SELECT DISTINCT ZoneId FROM Stats WHERE EventId=E.Id)'; + $value = '(SELECT * FROM Stats WHERE EventId=E.Id AND ZoneId='.$value.')'; break; case 'MonitorName': case 'Name': @@ -1272,8 +1269,11 @@ ZM\Logger::Debug("Term: " . print_r($term,true)); case 'Date': case 'StartDate': case 'EndDate': - if ( $value != 'NULL' ) - $value = 'to_days(\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; + if ( $value == 'CURDATE()' or $value == 'NOW()' ) { + $value = 'to_days('.$value.')'; + } else if ( $value != 'NULL' ) { + $value = 'to_days(\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; + } break; case 'Time': case 'StartTime': @@ -1308,10 +1308,13 @@ ZM\Logger::Debug("Term: " . print_r($term,true)); break; case '=[]' : case 'IN' : - $filter['sql'] .= ' in ('.join(',', $valueList).')'; + $filter['sql'] .= ' IN ('.join(',', $valueList).')'; break; case '![]' : $filter['sql'] .= ' not in ('.join(',', $valueList).')'; + break; + case 'EXISTS' : + $filter['sql'] .= ' EXISTS ' .$value; break; case 'IS' : if ( $value == 'Odd' ) { From 52dfbc92e91d4e6eeb401cd7ae0a5130fb4c5a08 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 24 Jan 2020 11:10:02 -0500 Subject: [PATCH 4/6] put back operator filtering in parseRows. Do it for all attrs. Fix Zone name in AlarmedZoneId attr --- web/skins/classic/views/js/filter.js | 32 +++++++++++++--------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/web/skins/classic/views/js/filter.js b/web/skins/classic/views/js/filter.js index ce20a576e..dd2c8e8c0 100644 --- a/web/skins/classic/views/js/filter.js +++ b/web/skins/classic/views/js/filter.js @@ -209,7 +209,7 @@ function parseRows(rows) { for ( zone_id in zones ) { var zone = zones[zone_id]; if ( monitor_id == zone.MonitorId ) { - zoneSelect.append(''); + zoneSelect.append(''); } } // end foreach zone } // end foreach monitor @@ -251,25 +251,23 @@ function parseRows(rows) { var monitorVal = inputTds.eq(4).children().val(); inputTds.eq(4).html(monitorSelect).children().val(monitorVal); } else { // Reset to regular text field and operator for everything that isn't special -if ( 0 ) { - // We don't actually do anything different in terms of operators. So why do it for text? - var opSelect = $j('').attr('name', queryPrefix + rowNum + '][op]').attr('id', queryPrefix + rowNum + '][op]'); - var opVal = inputTds.eq(3).children().val(); - if ( ! opVal ) { - // Default to equals so that something gets selected - console.log("No value for operator. Defaulting to ="); - opVal = '='; - } - for ( var key in opTypes ) { - opSelect.append(''); - } - inputTds.eq(3).html(opSelect).children().val(opVal).chosen({width: "101%"}); -} - var textInput = $j('').attr('type', 'text').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]'); var textVal = inputTds.eq(4).children().val(); inputTds.eq(4).html(textInput).children().val(textVal); } + + // Validate the operator + var opSelect = $j('').attr('name', queryPrefix + rowNum + '][op]').attr('id', queryPrefix + rowNum + '][op]'); + var opVal = inputTds.eq(3).children().val(); + if ( ! opVal ) { + // Default to equals so that something gets selected + console.log("No value for operator. Defaulting to ="); + opVal = '='; + } + for ( var key in opTypes ) { + opSelect.append(''); + } + inputTds.eq(3).html(opSelect).children().val(opVal).chosen({width: "101%"}); if ( attr.endsWith('DateTime') ) { //Start/End DateTime inputTds.eq(4).children().datetimepicker({timeFormat: "HH:mm:ss", dateFormat: "yy-mm-dd", maxDate: 0, constrainInput: false}); } else if ( attr.endsWith('Date') ) { //Start/End Date @@ -303,7 +301,7 @@ function addTerm( element ) { var newRow = row.clone().insertAfter(row); row.find('select').chosen({width: '101%'}); newRow.find('select').each( function() { //reset new row to default - this[0].selected = 'selected'; + this[0].selected = 'selected'; }).chosen({width: '101%'}); newRow.find('input[type="text"]').val(''); newRow[0].querySelectorAll("button[data-on-click-this]").forEach(function attachOnClick(el) { From 3b1458bb885929c78071b645eff3f2060eba3058 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 19 Feb 2020 14:17:44 -0500 Subject: [PATCH 5/6] add Emailed status to events list --- web/skins/classic/views/events.php | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/web/skins/classic/views/events.php b/web/skins/classic/views/events.php index ba57769be..cfbe90383 100644 --- a/web/skins/classic/views/events.php +++ b/web/skins/classic/views/events.php @@ -191,21 +191,25 @@ while ( $event_row = dbFetchNext($results) ) { ?> Archived()) echo ' class="archived"' ?>> '.$event->Id().($event->Archived()?'*':'') ?> - '.validHtmlStr($event->Name()).($event->Archived()?'*':'') ?> + '.validHtmlStr($event->Name()).($event->Archived()?'*':'') ?>
+Emailed() ) + echo 'Emailed '; +?> + MonitorId(), 'zmMonitor'.$event->MonitorId(), 'monitor', $event->MonitorName(), canEdit( 'Monitors' ) ) ?> Id(), 'zmEventDetail', 'eventdetail', validHtmlStr($event->Cause()), canEdit( 'Events' ), 'title="'.htmlspecialchars($event->Notes()).'"' ) ?> Notes()) { + if ( $event->Notes() ) { # if notes include detection objects, then link it to objdetect.jpg - if (strpos($event->Notes(),'detected:')!== false){ + if ( strpos($event->Notes(), 'detected:') !== false ) { # make a link echo makePopupLink( '?view=image&eid='.$event->Id().'&fid=objdetect', 'zmImage', array('image', reScale($event->Width(), $scale), reScale($event->Height(), $scale)), - "
".$event->Notes()."
"); - } - elseif ($event->Notes() != 'Forced Web: ') { - echo "
".$event->Notes()."
"; + '
'.$event->Notes().'
'); + } else if ( $event->Notes() != 'Forced Web: ' ) { + echo '
'.$event->Notes().'
'; } } ?> From a599028de46a74cdf93dd1c961a99bbf87cacb0d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 19 Feb 2020 14:18:28 -0500 Subject: [PATCH 6/6] If Filter attr is AlarmedZoneId we can only support the EXISTS operator --- scripts/ZoneMinder/lib/ZoneMinder/Filter.pm | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm index 6a46ee5d4..21803d76a 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm @@ -1,6 +1,6 @@ # ========================================================================== # -# ZoneMinder Filter Module, $Date$, $Revision$ +# ZoneMinder Filter Module # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or @@ -162,7 +162,9 @@ sub Sql { my $value = $term->{val}; my @value_list; if ( $term->{attr} ) { - if ( $term->{attr} =~ /^Monitor/ ) { + if ( $term->{attr} eq 'AlarmedZoneId' ) { + $term->{op} = 'EXISTS'; + } elsif ( $term->{attr} =~ /^Monitor/ ) { my ( $temp_attr_name ) = $term->{attr} =~ /^Monitor(.+)$/; $self->{Sql} .= 'M.'.$temp_attr_name; } elsif ( $term->{attr} eq 'ServerId' or $term->{attr} eq 'MonitorServerId' ) { @@ -214,7 +216,10 @@ sub Sql { ( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/; foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) { - if ( $term->{attr} =~ /^MonitorName/ ) { + + if ( $term->{attr} eq 'AlarmedZoneId' ) { + $value = '(SELECT * FROM Stats WHERE EventId=E.Id AND ZoneId='.$value.')'; + } elsif ( $term->{attr} =~ /^MonitorName/ ) { $value = "'$temp_value'"; } elsif ( $term->{attr} =~ /ServerId/) { Debug("ServerId, temp_value is ($temp_value) ($ZoneMinder::Config::Config{ZM_SERVER_ID})"); @@ -256,6 +261,8 @@ sub Sql { } elsif ( $term->{attr} eq 'Date' or $term->{attr} eq 'StartDate' or $term->{attr} eq 'EndDate' ) { if ( $temp_value eq 'NULL' ) { $value = $temp_value; + } elsif ( $temp_value eq 'CURDATE()' or $temp_value eq 'NOW()' ) { + $value = 'to_days('.$temp_value.')'; } else { $value = DateTimeToSQL($temp_value); if ( !$value ) { @@ -294,6 +301,8 @@ sub Sql { } else { $self->{Sql} .= " IS $value"; } + } elsif ( $term->{op} eq 'EXISTS' ) { + $self->{Sql} .= " EXISTS $value"; } elsif ( $term->{op} eq 'IS NOT' ) { $self->{Sql} .= " IS NOT $value"; } elsif ( $term->{op} eq '=[]' ) {