From 0ffb5e153c23642282d8aae0772d01b8fab8477c Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Sat, 14 Nov 2020 17:33:18 -0600 Subject: [PATCH 1/2] modify FilterTerm to support Frames view searches --- web/ajax/frames.php | 638 ++++++++++++++++++++++++++---------- web/includes/FilterTerm.php | 16 + 2 files changed, 481 insertions(+), 173 deletions(-) diff --git a/web/ajax/frames.php b/web/ajax/frames.php index 440c9769e..42f939128 100644 --- a/web/ajax/frames.php +++ b/web/ajax/frames.php @@ -1,189 +1,481 @@ "search text" pairs -// Bootstrap table sends json_ecoded array, which we must decode -$advsearch = isset($_REQUEST['advsearch']) ? json_decode($_REQUEST['advsearch'], JSON_OBJECT_AS_ARRAY) : array(); - -// Sort specifies the name of the column to sort on -$sort = 'FrameId'; -if ( isset($_REQUEST['sort']) ) { - $sort = $_REQUEST['sort']; -} - -// Offset specifies the starting row to return, used for pagination -$offset = 0; -if ( isset($_REQUEST['offset']) ) { - if ( ( !is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']) ) ) { - ZM\Error('Invalid value for offset: ' . $_REQUEST['offset']); - } else { - $offset = $_REQUEST['offset']; +function getFilterQueryConjunctionTypes() { + if ( !isset($validConjunctionTypes ) ) { + $validConjunctionTypes = array( + 'and' => translate('ConjAnd'), + 'or' => translate('ConjOr') + ); } + return $validConjunctionTypes; } -// Order specifies the sort direction, either asc or desc -$order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc')) ? 'ASC' : 'DESC'; -// Limit specifies the number of rows to return -$limit = 0; -if ( isset($_REQUEST['limit']) ) { - if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) { - ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']); - } else { - $limit = $_REQUEST['limit']; - } -} +class FilterTerm { + public $filter; + public $index; + public $attr; + public $op; + public $val; + public $values; + public $cnj; + public $obr; + public $cbr; -// -// MAIN LOOP -// -// Only one supported task at the moment -switch ( $task ) { - case 'query' : - $data = queryRequest($eid, $search, $advsearch, $sort, $offset, $order, $limit); - break; - default : - ZM\Fatal("Unrecognised task '$task'"); -} // end switch task + public function __construct($filter = null, $term = NULL, $index=0) { + $this->filter = $filter; + $validConjunctionTypes = getFilterQueryConjunctionTypes(); -ajaxResponse($data); - -// -// FUNCTION DEFINITIONS -// - -function queryRequest($eid, $search, $advsearch, $sort, $offset, $order, $limit) { - - $data = array( - 'total' => 0, - 'totalNotFiltered' => 0, - 'rows' => array(), - 'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG) - ); - - // The names of the dB columns in the events table we are interested in - $columns = array('EventId', 'FrameId', 'Type', 'TimeStamp', 'Delta', 'Score'); - - if ( !in_array($sort, $columns) ) { - ZM\Error('Invalid sort field: ' . $sort); - $sort = 'FrameId'; - } - - $Event = new ZM\Event($eid); - $Monitor = $Event->Monitor(); - $values = array(); - $likes = array(); - $where = 'WHERE EventId = '.$eid; - - $sql = 'SELECT * FROM `Frames` '.$where.' ORDER BY '.$sort.' '.$order; - - //ZM\Debug('Calling the following sql query: ' .$sql); - - $unfiltered_rows = array(); - $frame_ids = array(); - require_once('includes/Frame.php'); - foreach ( dbFetchAll($sql, NULL, $values) as $row ) { - $frame = new ZM\Frame($row); - $frame_ids[] = $frame->Id(); - $unfiltered_rows[] = $row; - } - - ZM\Debug('Have ' . count($unfiltered_rows) . ' frames matching base filter.'); - - $filtered_rows = null; - require_once('includes/Filter.php'); - if ( count($advsearch) or $search != '' ) { - $search_filter = new ZM\Filter(); - $search_filter = $search_filter->addTerm(array('cnj'=>'and', 'attr'=>'Id', 'op'=>'IN', 'val'=>$frame_ids)); - - // There are two search bars in the log view, normal and advanced - // Making an exuctive decision to ignore the normal search, when advanced search is in use - // Alternatively we could try to do both - if ( count($advsearch) ) { - $terms = array(); - foreach ( $advsearch as $col=>$text ) { - $terms[] = array('cnj'=>'and', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$text); - } # end foreach col in advsearch - $terms[0]['obr'] = 1; - $terms[count($terms)-1]['cbr'] = 1; - $search_filter->addTerms($terms); - } else if ( $search != '' ) { - $search = '%' .$search. '%'; - $terms = array(); - foreach ( $columns as $col ) { - $terms[] = array('cnj'=>'or', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$search); + $this->index = $index; + $this->attr = $term['attr']; + $this->op = $term['op']; + $this->val = $term['val']; + if ( isset($term['cnj']) ) { + if ( array_key_exists($term['cnj'], $validConjunctionTypes) ) { + $this->cnj = $term['cnj']; + } else { + Warning('Invalid cnj ' . $term['cnj'].' in '.print_r($term, true)); } - $terms[0]['obr'] = 1; - $terms[0]['cnj'] = 'and'; - $terms[count($terms)-1]['cbr'] = 1; - $search_filter = $search_filter->addTerms($terms, array('obr'=>1, 'cbr'=>1, 'op'=>'OR')); - } # end if search - - $sql = 'SELECT * FROM `Frames` WHERE '.$search_filter->sql().' ORDER BY ' .$sort. ' ' .$order; - $filtered_rows = dbFetchAll($sql); - ZM\Debug('Have ' . count($filtered_rows) . ' frames matching search filter.'); - } else { - $filtered_rows = $unfiltered_rows; - } # end if search_filter->terms() > 1 - - $returned_rows = array(); - foreach ( array_slice($filtered_rows, $offset, $limit) as $row ) { - if ( ZM_WEB_LIST_THUMBS ) { - $base_img_src = '?view=image&fid=' .$row['Id']; - $ratio_factor = $Monitor->ViewHeight() / $Monitor->ViewWidth(); - $thmb_width = ZM_WEB_LIST_THUMB_WIDTH ? 'width='.ZM_WEB_LIST_THUMB_WIDTH : ''; - $thmb_height = 'height="'.( ZM_WEB_LIST_THUMB_HEIGHT ? ZM_WEB_LIST_THUMB_HEIGHT : ZM_WEB_LIST_THUMB_WIDTH*$ratio_factor ) .'"'; - $thmb_fn = 'filename=' .$Event->MonitorId(). '_' .$row['EventId']. '_' .$row['FrameId']. '.jpg'; - $img_src = join('&', array_filter(array($base_img_src, $thmb_width, $thmb_height, $thmb_fn))); - $full_img_src = join('&', array_filter(array($base_img_src, $thmb_fn))); - $frame_src = '?view=frame&eid=' .$row['EventId']. '&fid=' .$row['FrameId']; - - $row['Thumbnail'] = ''; } - $returned_rows[] = $row; - } # end foreach row matching search - $data['rows'] = $returned_rows; + if ( isset($term['obr']) ) { + if ( (string)(int)$term['obr'] == $term['obr'] ) { + $this->obr = $term['obr']; + } else { + Warning('Invalid obr ' . $term['obr'] . ' in ' . print_r($term, true)); + } + } + if ( isset($term['cbr']) ) { + if ( (string)(int)$term['cbr'] == $term['cbr'] ) { + $this->cbr = $term['cbr']; + } else { + Warning('Invalid cbr ' . $term['cbr'] . ' in ' . print_r($term, true)); + } + } + } # end function __construct - # totalNotFiltered must equal total, except when either search bar has been used - $data['totalNotFiltered'] = count($unfiltered_rows); - if ( $search != '' || count($advsearch) ) { - $data['total'] = count($filtered_rows); - } else { - $data['total'] = $data['totalNotFiltered']; + # Returns an array of values. AS term->value can be a list, we will break it apart, remove quotes etc + public function sql_values() { + $values = array(); + if ( !isset($this->val) ) { + Logger::Warning('No value in term'.$this->attr); + return $values; + } + + $vals = is_array($this->val) ? $this->val : preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $this->val)); + foreach ( $vals as $value ) { + + switch ( $this->attr ) { + + case 'AlarmedZoneId': + $value = '(SELECT * FROM Stats WHERE EventId=E.Id AND ZoneId='.$value.')'; + break; + case 'ExistsInFileSystem': + $value = ''; + break; + case 'DiskPercent': + $value = ''; + break; + case 'MonitorName': + case 'MonitorName': + case 'Name': + case 'Cause': + case 'Notes': + if ( strstr($this->op, 'LIKE') and ! strstr($this->val, '%' ) ) { + $value = '%'.$value.'%'; + } + $value = dbEscape($value); + break; + case 'MonitorServerId': + case 'FilterServerId': + case 'StorageServerId': + case 'ServerId': + if ( $value == 'ZM_SERVER_ID' ) { + $value = ZM_SERVER_ID; + } else if ( $value == 'NULL' ) { + + } else { + $value = dbEscape($value); + } + break; + case 'StorageId': + if ( $value != 'NULL' ) { + $value = dbEscape($value); + } + break; + case 'DateTime': + case 'StartDateTime': + case 'EndDateTime': + if ( $value != 'NULL' ) + $value = '\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\''; + break; + case 'Date': + case 'StartDate': + case 'EndDate': + 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': + case 'EndTime': + if ( $value != 'NULL' ) + $value = 'extract(hour_second from \''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; + break; + default : + if ( $value == 'Odd' ) { + $value = 1; + } else if ( $value == 'Even' ) { + $value = 0; + } else if ( $value != 'NULL' ) + $value = dbEscape($value); + break; + } + $values[] = $value; + } // end foreach value + return $values; + } # end function sql_values + + public function sql_operator() { + switch ( $this->attr ) { + case 'AlarmZoneId': + return ' EXISTS '; + case 'ExistsInFileSystem': + case 'DiskPercent': + return ''; + } + + + switch ( $this->op ) { + case '=' : + case '!=' : + case '>=' : + case '>' : + case '<' : + case '<=' : + case 'LIKE' : + case 'NOT LIKE': + return ' '.$this->op.' '; + case '=~' : + return ' regexp '; + case '!~' : + return ' not regexp '; + case '=[]' : + case 'IN' : + return ' IN '; + case '![]' : + return ' NOT IN '; + case 'EXISTS' : + return ' EXISTS '; + case 'IS' : + # Odd will be replaced with 1 + # Even will be replaced with 0 + if ( $this->val == 'Odd' or $this->val == 'Even' ) { + return ' % 2 = '; + } else { + return ' IS '; + } + case 'IS NOT' : + if ( $this->val == 'Odd' or $this->val == 'Even' ) { + return ' % 2 = '; + } + return ' IS NOT '; + default: + Warning('Invalid operator in filter: ' . print_r($this->op, true)); + } // end switch op + } # end public function sql_operator + + /* Some terms don't have related SQL */ + public function sql() { + + $sql = ''; + if ( isset($this->cnj) ) { + $sql .= ' '.$this->cnj.' '; + } + if ( isset($this->obr) ) { + $sql .= ' '.str_repeat('(', $this->obr).' '; + } + + switch ( $this->attr ) { + case 'ExistsInFileSystem': + case 'DiskPercent': + $sql .= 'TRUE /*'.$this->attr.'*/'; + break; + case 'MonitorName': + $sql .= 'M.Name'; + break; + case 'ServerId': + case 'MonitorServerId': + $sql .= 'M.ServerId'; + break; + case 'StorageServerId': + $sql .= 'S.ServerId'; + break; + case 'FilterServerId': + $sql .= ZM_SERVER_ID; + break; + # Unspecified start or end, so assume start, this is to support legacy filters + case 'DateTime': + $sql .= 'E.StartDateTime'; + break; + case 'Date': + $sql .= 'to_days(E.StartDateTime)'; + break; + case 'Time': + $sql .= 'extract(hour_second FROM E.StartDateTime)'; + break; + case 'Weekday': + $sql .= 'weekday(E.StartDateTime)'; + break; + # Starting Time + case 'StartDateTime': + $sql .= 'E.StartDateTime'; + break; + case 'FrameId': + $sql .= 'Id'; + break; + case 'Type': + case 'TimeStamp': + case 'Delta': + case 'Score': + $sql .= $this->attr; + break; + case 'FramesEventId': + $sql .= 'F.EventId'; + break; + case 'StartDate': + $sql .= 'to_days(E.StartDateTime)'; + break; + case 'StartTime': + $sql .= 'extract(hour_second FROM E.StartDateTime)'; + break; + case 'StartWeekday': + $sql .= 'weekday(E.StartDateTime)'; + break; + # Ending Time + case 'EndDateTime': + $sql .= 'E.EndDateTime'; + break; + case 'EndDate': + $sql .= 'to_days(E.EndDateTime)'; + break; + case 'EndTime': + $sql .= 'extract(hour_second FROM E.EndDateTime)'; + break; + case 'EndWeekday': + $sql .= 'weekday(E.EndDateTime)'; + break; + case 'Emailed': + case 'Id': + case 'Name': + case 'DiskSpace': + case 'MonitorId': + case 'StorageId': + case 'SecondaryStorageId': + case 'Length': + case 'Frames': + case 'AlarmFrames': + case 'TotScore': + case 'AvgScore': + case 'MaxScore': + case 'Cause': + case 'Notes': + case 'StateId': + case 'Archived': + $sql .= 'E.'.$this->attr; + } + $sql .= $this->sql_operator(); + $values = $this->sql_values(); + if ( (count($values) > 1) or ($this->op == 'IN') or ($this->op == 'NOT IN') ) { + $sql .= '('.join(',', $values).')'; + } else { + $sql .= $values[0]; + } + + if ( isset($this->cbr) ) { + $sql .= ' '.str_repeat(')', $this->cbr).' '; + } + return $sql; + } # end public function sql + + public function querystring($objectname='filter', $querySep='&') { + # We don't validate the term parameters here + $query = ''; + if ( $this->cnj ) + $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][cnj]').'='.$this->cnj; + if ( $this->obr ) + $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][obr]').'='.$this->obr; + + $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][attr]').'='.urlencode($this->attr); + $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][op]').'='.urlencode($this->op); + $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][val]').'='.urlencode($this->val); + if ( $this->cbr ) + $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][cbr]').'='.$this->cbr; + return $query; + } # end public function querystring + + public function hidden_fields() { + $html =''; + if ( $this->cnj ) + $html .= ''.PHP_EOL; + + if ( $this->obr ) + $html .= ''.PHP_EOL; + + # attr should have been already validation, so shouldn't need htmlspecialchars + $html .= ''.PHP_EOL; + $html .= ''.PHP_EOL; + $html .= ''.PHP_EOL; + if ( $this->cbr ) + $html .= ''.PHP_EOL; + + return $html; + } # end public function hiddens_fields + + public function test($event=null) { + if ( !isset($event) ) { + # Is a Pre Condition + Debug("Testing " . $this->attr); + if ( $this->attr == 'DiskPercent' ) { + # The logic on this is really ugly. We are going to treat it as an OR + foreach ( $this->filter->get_StorageAreas() as $storage ) { + $string_to_eval = 'return $storage->disk_usage_percent() '.$this->op.' '.$this->val.';'; + try { + $ret = eval($string_to_eval); + Debug("Evalled $string_to_eval = $ret"); + if ( $ret ) + return true; + } catch ( Throwable $t ) { + Error('Failed evaluating '.$string_to_eval); + return false; + } + } # end foreach Storage Area + } else if ( $this->attr == 'SystemLoad' ) { + $string_to_eval = 'return getLoad() '.$this->op.' '.$this->val.';'; + try { + $ret = eval($string_to_eval); + Debug("Evaled $string_to_eval = $ret"); + if ( $ret ) + return true; + } catch ( Throwable $t ) { + Error('Failed evaluating '.$string_to_eval); + return false; + } + } else { + Error('testing unsupported pre term ' . $this->attr); + } + } else { + # Is a Post Condition + if ( $this->attr == 'ExistsInFileSystem' ) { + if ( + ($this->op == 'IS' and $this->val == 'True') + or + ($this->op == 'IS NOT' and $this->val == 'False') + ) { + return file_exists($event->Path()); + } else { + return !file_exists($event->Path()); + } + } else if ( $this->attr == 'DiskPercent' ) { + $string_to_eval = 'return $event->Storage()->disk_usage_percent() '.$this->op.' '.$this->val.';'; + try { + $ret = eval($string_to_eval); + Debug("Evalled $string_to_eval = $ret"); + if ( $ret ) + return true; + } catch ( Throwable $t ) { + Error('Failed evaluating '.$string_to_eval); + return false; + } + } else if ( $this->attr == 'DiskBlocks' ) { + $string_to_eval = 'return $event->Storage()->disk_usage_blocks() '.$this->op.' '.$this->val.';'; + try { + $ret = eval($string_to_eval); + Debug("Evalled $string_to_eval = $ret"); + if ( $ret ) + return true; + } catch ( Throwable $t ) { + Error('Failed evaluating '.$string_to_eval); + return false; + } + } else { + Error('testing unsupported post term ' . $this->attr); + } + } + return false; + } + + public function is_pre_sql() { + if ( $this->attr == 'DiskPercent' ) + return true; + if ( $this->attr == 'DiskBlocks' ) + return true; + return false; } - return $data; -} + public function is_post_sql() { + if ( $this->attr == 'ExistsInFileSystem' ) { + return true; + } + return false; + } + + public static function is_valid_attr($attr) { + $attrs = array( + 'Score', + 'Delta', + 'TimeStamp', + 'Type', + 'FrameId', + 'EventId', + 'ExistsInFileSystem', + 'Emailed', + 'DiskSpace', + 'DiskPercent', + 'DiskBlocks', + 'MonitorName', + 'ServerId', + 'MonitorServerId', + 'StorageServerId', + 'FilterServerId', + 'DateTime', + 'Date', + 'Time', + 'Weekday', + 'StartDateTime', + 'FramesId', + 'FramesEventId', + 'StartDate', + 'StartTime', + 'StartWeekday', + 'EndDateTime', + 'EndDate', + 'EndTime', + 'EndWeekday', + 'Id', + 'Name', + 'MonitorId', + 'StorageId', + 'SecondaryStorageId', + 'Length', + 'Frames', + 'AlarmFrames', + 'TotScore', + 'AvgScore', + 'MaxScore', + 'Cause', + 'Notes', + 'StateId', + 'Archived' + ); + return in_array($attr, $attrs); + } +} # end class FilterTerm + +?> diff --git a/web/includes/FilterTerm.php b/web/includes/FilterTerm.php index 6b41a2e1d..42f939128 100644 --- a/web/includes/FilterTerm.php +++ b/web/includes/FilterTerm.php @@ -237,6 +237,15 @@ class FilterTerm { case 'StartDateTime': $sql .= 'E.StartDateTime'; break; + case 'FrameId': + $sql .= 'Id'; + break; + case 'Type': + case 'TimeStamp': + case 'Delta': + case 'Score': + $sql .= $this->attr; + break; case 'FramesEventId': $sql .= 'F.EventId'; break; @@ -419,6 +428,12 @@ class FilterTerm { public static function is_valid_attr($attr) { $attrs = array( + 'Score', + 'Delta', + 'TimeStamp', + 'Type', + 'FrameId', + 'EventId', 'ExistsInFileSystem', 'Emailed', 'DiskSpace', @@ -434,6 +449,7 @@ class FilterTerm { 'Time', 'Weekday', 'StartDateTime', + 'FramesId', 'FramesEventId', 'StartDate', 'StartTime', From a2b5271835654c559850214b12ac0c9f6892de6f Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Sat, 14 Nov 2020 17:34:59 -0600 Subject: [PATCH 2/2] fix copy/paste typo --- web/ajax/frames.php | 638 ++++++++++++-------------------------------- 1 file changed, 173 insertions(+), 465 deletions(-) diff --git a/web/ajax/frames.php b/web/ajax/frames.php index 42f939128..e85988dbc 100644 --- a/web/ajax/frames.php +++ b/web/ajax/frames.php @@ -1,481 +1,189 @@ translate('ConjAnd'), - 'or' => translate('ConjOr') - ); - } - return $validConjunctionTypes; +if ( !canView('Events') ) $message = 'Insufficient permissions to view frames for user '.$user['Username']; + +// task must be set +if ( empty($_REQUEST['task']) ) { + $message = 'This request requires a task to be set'; +// query is the only supported task at the moment +} else if ( $_REQUEST['task'] != 'query' ) { + $message = 'Unrecognised task '.$_REQUEST['task']; + } else { + $task = $_REQUEST['task']; } +if ( empty($_REQUEST['eid']) ) { + $message = 'No event id supplied'; +} else { + $eid = validInt($_REQUEST['eid']); +} -class FilterTerm { - public $filter; - public $index; - public $attr; - public $op; - public $val; - public $values; - public $cnj; - public $obr; - public $cbr; +if ( $message ) { + ajaxError($message); + return; +} +// Search contains a user entered string to search on +$search = isset($_REQUEST['search']) ? $_REQUEST['search'] : ''; - public function __construct($filter = null, $term = NULL, $index=0) { - $this->filter = $filter; - $validConjunctionTypes = getFilterQueryConjunctionTypes(); +// Advanced search contains an array of "column name" => "search text" pairs +// Bootstrap table sends json_ecoded array, which we must decode +$advsearch = isset($_REQUEST['advsearch']) ? json_decode($_REQUEST['advsearch'], JSON_OBJECT_AS_ARRAY) : array(); - $this->index = $index; - $this->attr = $term['attr']; - $this->op = $term['op']; - $this->val = $term['val']; - if ( isset($term['cnj']) ) { - if ( array_key_exists($term['cnj'], $validConjunctionTypes) ) { - $this->cnj = $term['cnj']; - } else { - Warning('Invalid cnj ' . $term['cnj'].' in '.print_r($term, true)); - } - } +// Sort specifies the name of the column to sort on +$sort = 'FrameId'; +if ( isset($_REQUEST['sort']) ) { + $sort = $_REQUEST['sort']; +} - if ( isset($term['obr']) ) { - if ( (string)(int)$term['obr'] == $term['obr'] ) { - $this->obr = $term['obr']; - } else { - Warning('Invalid obr ' . $term['obr'] . ' in ' . print_r($term, true)); - } - } - if ( isset($term['cbr']) ) { - if ( (string)(int)$term['cbr'] == $term['cbr'] ) { - $this->cbr = $term['cbr']; - } else { - Warning('Invalid cbr ' . $term['cbr'] . ' in ' . print_r($term, true)); - } - } - } # end function __construct - - # Returns an array of values. AS term->value can be a list, we will break it apart, remove quotes etc - public function sql_values() { - $values = array(); - if ( !isset($this->val) ) { - Logger::Warning('No value in term'.$this->attr); - return $values; - } - - $vals = is_array($this->val) ? $this->val : preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $this->val)); - foreach ( $vals as $value ) { - - switch ( $this->attr ) { - - case 'AlarmedZoneId': - $value = '(SELECT * FROM Stats WHERE EventId=E.Id AND ZoneId='.$value.')'; - break; - case 'ExistsInFileSystem': - $value = ''; - break; - case 'DiskPercent': - $value = ''; - break; - case 'MonitorName': - case 'MonitorName': - case 'Name': - case 'Cause': - case 'Notes': - if ( strstr($this->op, 'LIKE') and ! strstr($this->val, '%' ) ) { - $value = '%'.$value.'%'; - } - $value = dbEscape($value); - break; - case 'MonitorServerId': - case 'FilterServerId': - case 'StorageServerId': - case 'ServerId': - if ( $value == 'ZM_SERVER_ID' ) { - $value = ZM_SERVER_ID; - } else if ( $value == 'NULL' ) { - - } else { - $value = dbEscape($value); - } - break; - case 'StorageId': - if ( $value != 'NULL' ) { - $value = dbEscape($value); - } - break; - case 'DateTime': - case 'StartDateTime': - case 'EndDateTime': - if ( $value != 'NULL' ) - $value = '\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\''; - break; - case 'Date': - case 'StartDate': - case 'EndDate': - 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': - case 'EndTime': - if ( $value != 'NULL' ) - $value = 'extract(hour_second from \''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; - break; - default : - if ( $value == 'Odd' ) { - $value = 1; - } else if ( $value == 'Even' ) { - $value = 0; - } else if ( $value != 'NULL' ) - $value = dbEscape($value); - break; - } - $values[] = $value; - } // end foreach value - return $values; - } # end function sql_values - - public function sql_operator() { - switch ( $this->attr ) { - case 'AlarmZoneId': - return ' EXISTS '; - case 'ExistsInFileSystem': - case 'DiskPercent': - return ''; - } - - - switch ( $this->op ) { - case '=' : - case '!=' : - case '>=' : - case '>' : - case '<' : - case '<=' : - case 'LIKE' : - case 'NOT LIKE': - return ' '.$this->op.' '; - case '=~' : - return ' regexp '; - case '!~' : - return ' not regexp '; - case '=[]' : - case 'IN' : - return ' IN '; - case '![]' : - return ' NOT IN '; - case 'EXISTS' : - return ' EXISTS '; - case 'IS' : - # Odd will be replaced with 1 - # Even will be replaced with 0 - if ( $this->val == 'Odd' or $this->val == 'Even' ) { - return ' % 2 = '; - } else { - return ' IS '; - } - case 'IS NOT' : - if ( $this->val == 'Odd' or $this->val == 'Even' ) { - return ' % 2 = '; - } - return ' IS NOT '; - default: - Warning('Invalid operator in filter: ' . print_r($this->op, true)); - } // end switch op - } # end public function sql_operator - - /* Some terms don't have related SQL */ - public function sql() { - - $sql = ''; - if ( isset($this->cnj) ) { - $sql .= ' '.$this->cnj.' '; - } - if ( isset($this->obr) ) { - $sql .= ' '.str_repeat('(', $this->obr).' '; - } - - switch ( $this->attr ) { - case 'ExistsInFileSystem': - case 'DiskPercent': - $sql .= 'TRUE /*'.$this->attr.'*/'; - break; - case 'MonitorName': - $sql .= 'M.Name'; - break; - case 'ServerId': - case 'MonitorServerId': - $sql .= 'M.ServerId'; - break; - case 'StorageServerId': - $sql .= 'S.ServerId'; - break; - case 'FilterServerId': - $sql .= ZM_SERVER_ID; - break; - # Unspecified start or end, so assume start, this is to support legacy filters - case 'DateTime': - $sql .= 'E.StartDateTime'; - break; - case 'Date': - $sql .= 'to_days(E.StartDateTime)'; - break; - case 'Time': - $sql .= 'extract(hour_second FROM E.StartDateTime)'; - break; - case 'Weekday': - $sql .= 'weekday(E.StartDateTime)'; - break; - # Starting Time - case 'StartDateTime': - $sql .= 'E.StartDateTime'; - break; - case 'FrameId': - $sql .= 'Id'; - break; - case 'Type': - case 'TimeStamp': - case 'Delta': - case 'Score': - $sql .= $this->attr; - break; - case 'FramesEventId': - $sql .= 'F.EventId'; - break; - case 'StartDate': - $sql .= 'to_days(E.StartDateTime)'; - break; - case 'StartTime': - $sql .= 'extract(hour_second FROM E.StartDateTime)'; - break; - case 'StartWeekday': - $sql .= 'weekday(E.StartDateTime)'; - break; - # Ending Time - case 'EndDateTime': - $sql .= 'E.EndDateTime'; - break; - case 'EndDate': - $sql .= 'to_days(E.EndDateTime)'; - break; - case 'EndTime': - $sql .= 'extract(hour_second FROM E.EndDateTime)'; - break; - case 'EndWeekday': - $sql .= 'weekday(E.EndDateTime)'; - break; - case 'Emailed': - case 'Id': - case 'Name': - case 'DiskSpace': - case 'MonitorId': - case 'StorageId': - case 'SecondaryStorageId': - case 'Length': - case 'Frames': - case 'AlarmFrames': - case 'TotScore': - case 'AvgScore': - case 'MaxScore': - case 'Cause': - case 'Notes': - case 'StateId': - case 'Archived': - $sql .= 'E.'.$this->attr; - } - $sql .= $this->sql_operator(); - $values = $this->sql_values(); - if ( (count($values) > 1) or ($this->op == 'IN') or ($this->op == 'NOT IN') ) { - $sql .= '('.join(',', $values).')'; - } else { - $sql .= $values[0]; - } - - if ( isset($this->cbr) ) { - $sql .= ' '.str_repeat(')', $this->cbr).' '; - } - return $sql; - } # end public function sql - - public function querystring($objectname='filter', $querySep='&') { - # We don't validate the term parameters here - $query = ''; - if ( $this->cnj ) - $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][cnj]').'='.$this->cnj; - if ( $this->obr ) - $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][obr]').'='.$this->obr; - - $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][attr]').'='.urlencode($this->attr); - $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][op]').'='.urlencode($this->op); - $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][val]').'='.urlencode($this->val); - if ( $this->cbr ) - $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][cbr]').'='.$this->cbr; - return $query; - } # end public function querystring - - public function hidden_fields() { - $html =''; - if ( $this->cnj ) - $html .= ''.PHP_EOL; - - if ( $this->obr ) - $html .= ''.PHP_EOL; - - # attr should have been already validation, so shouldn't need htmlspecialchars - $html .= ''.PHP_EOL; - $html .= ''.PHP_EOL; - $html .= ''.PHP_EOL; - if ( $this->cbr ) - $html .= ''.PHP_EOL; - - return $html; - } # end public function hiddens_fields - - public function test($event=null) { - if ( !isset($event) ) { - # Is a Pre Condition - Debug("Testing " . $this->attr); - if ( $this->attr == 'DiskPercent' ) { - # The logic on this is really ugly. We are going to treat it as an OR - foreach ( $this->filter->get_StorageAreas() as $storage ) { - $string_to_eval = 'return $storage->disk_usage_percent() '.$this->op.' '.$this->val.';'; - try { - $ret = eval($string_to_eval); - Debug("Evalled $string_to_eval = $ret"); - if ( $ret ) - return true; - } catch ( Throwable $t ) { - Error('Failed evaluating '.$string_to_eval); - return false; - } - } # end foreach Storage Area - } else if ( $this->attr == 'SystemLoad' ) { - $string_to_eval = 'return getLoad() '.$this->op.' '.$this->val.';'; - try { - $ret = eval($string_to_eval); - Debug("Evaled $string_to_eval = $ret"); - if ( $ret ) - return true; - } catch ( Throwable $t ) { - Error('Failed evaluating '.$string_to_eval); - return false; - } - } else { - Error('testing unsupported pre term ' . $this->attr); - } - } else { - # Is a Post Condition - if ( $this->attr == 'ExistsInFileSystem' ) { - if ( - ($this->op == 'IS' and $this->val == 'True') - or - ($this->op == 'IS NOT' and $this->val == 'False') - ) { - return file_exists($event->Path()); - } else { - return !file_exists($event->Path()); - } - } else if ( $this->attr == 'DiskPercent' ) { - $string_to_eval = 'return $event->Storage()->disk_usage_percent() '.$this->op.' '.$this->val.';'; - try { - $ret = eval($string_to_eval); - Debug("Evalled $string_to_eval = $ret"); - if ( $ret ) - return true; - } catch ( Throwable $t ) { - Error('Failed evaluating '.$string_to_eval); - return false; - } - } else if ( $this->attr == 'DiskBlocks' ) { - $string_to_eval = 'return $event->Storage()->disk_usage_blocks() '.$this->op.' '.$this->val.';'; - try { - $ret = eval($string_to_eval); - Debug("Evalled $string_to_eval = $ret"); - if ( $ret ) - return true; - } catch ( Throwable $t ) { - Error('Failed evaluating '.$string_to_eval); - return false; - } - } else { - Error('testing unsupported post term ' . $this->attr); - } - } - return false; +// Offset specifies the starting row to return, used for pagination +$offset = 0; +if ( isset($_REQUEST['offset']) ) { + if ( ( !is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']) ) ) { + ZM\Error('Invalid value for offset: ' . $_REQUEST['offset']); + } else { + $offset = $_REQUEST['offset']; } - - public function is_pre_sql() { - if ( $this->attr == 'DiskPercent' ) - return true; - if ( $this->attr == 'DiskBlocks' ) - return true; - return false; +} + +// Order specifies the sort direction, either asc or desc +$order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc')) ? 'ASC' : 'DESC'; + +// Limit specifies the number of rows to return +$limit = 0; +if ( isset($_REQUEST['limit']) ) { + if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) { + ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']); + } else { + $limit = $_REQUEST['limit']; + } +} + +// +// MAIN LOOP +// + +// Only one supported task at the moment +switch ( $task ) { + case 'query' : + $data = queryRequest($eid, $search, $advsearch, $sort, $offset, $order, $limit); + break; + default : + ZM\Fatal("Unrecognised task '$task'"); +} // end switch task + +ajaxResponse($data); + +// +// FUNCTION DEFINITIONS +// + +function queryRequest($eid, $search, $advsearch, $sort, $offset, $order, $limit) { + + $data = array( + 'total' => 0, + 'totalNotFiltered' => 0, + 'rows' => array(), + 'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG) + ); + + // The names of the dB columns in the events table we are interested in + $columns = array('FrameId', 'Type', 'TimeStamp', 'Delta', 'Score'); + + if ( !in_array($sort, $columns) ) { + ZM\Error('Invalid sort field: ' . $sort); + $sort = 'FrameId'; } - public function is_post_sql() { - if ( $this->attr == 'ExistsInFileSystem' ) { - return true; + $Event = new ZM\Event($eid); + $Monitor = $Event->Monitor(); + $values = array(); + $likes = array(); + $where = 'WHERE EventId = '.$eid; + + $sql = 'SELECT * FROM `Frames` '.$where.' ORDER BY '.$sort.' '.$order; + + //ZM\Debug('Calling the following sql query: ' .$sql); + + $unfiltered_rows = array(); + $frame_ids = array(); + require_once('includes/Frame.php'); + foreach ( dbFetchAll($sql, NULL, $values) as $row ) { + $frame = new ZM\Frame($row); + $frame_ids[] = $frame->Id(); + $unfiltered_rows[] = $row; + } + + ZM\Debug('Have ' . count($unfiltered_rows) . ' frames matching base filter.'); + + $filtered_rows = null; + require_once('includes/Filter.php'); + if ( count($advsearch) or $search != '' ) { + $search_filter = new ZM\Filter(); + $search_filter = $search_filter->addTerm(array('cnj'=>'and', 'attr'=>'FrameId', 'op'=>'IN', 'val'=>$frame_ids)); + + // There are two search bars in the log view, normal and advanced + // Making an exuctive decision to ignore the normal search, when advanced search is in use + // Alternatively we could try to do both + if ( count($advsearch) ) { + $terms = array(); + foreach ( $advsearch as $col=>$text ) { + $terms[] = array('cnj'=>'and', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$text); + } # end foreach col in advsearch + $terms[0]['obr'] = 1; + $terms[count($terms)-1]['cbr'] = 1; + $search_filter->addTerms($terms); + } else if ( $search != '' ) { + $search = '%' .$search. '%'; + $terms = array(); + foreach ( $columns as $col ) { + $terms[] = array('cnj'=>'or', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$search); + } + $terms[0]['obr'] = 1; + $terms[0]['cnj'] = 'and'; + $terms[count($terms)-1]['cbr'] = 1; + $search_filter = $search_filter->addTerms($terms, array('obr'=>1, 'cbr'=>1, 'op'=>'OR')); + } # end if search + + $sql = 'SELECT * FROM `Frames` WHERE '.$search_filter->sql().' ORDER BY ' .$sort. ' ' .$order; + $filtered_rows = dbFetchAll($sql); + ZM\Debug('Have ' . count($filtered_rows) . ' frames matching search filter.'); + } else { + $filtered_rows = $unfiltered_rows; + } # end if search_filter->terms() > 1 + + $returned_rows = array(); + foreach ( array_slice($filtered_rows, $offset, $limit) as $row ) { + if ( ZM_WEB_LIST_THUMBS ) { + $base_img_src = '?view=image&fid=' .$row['Id']; + $ratio_factor = $Monitor->ViewHeight() / $Monitor->ViewWidth(); + $thmb_width = ZM_WEB_LIST_THUMB_WIDTH ? 'width='.ZM_WEB_LIST_THUMB_WIDTH : ''; + $thmb_height = 'height="'.( ZM_WEB_LIST_THUMB_HEIGHT ? ZM_WEB_LIST_THUMB_HEIGHT : ZM_WEB_LIST_THUMB_WIDTH*$ratio_factor ) .'"'; + $thmb_fn = 'filename=' .$Event->MonitorId(). '_' .$row['EventId']. '_' .$row['FrameId']. '.jpg'; + $img_src = join('&', array_filter(array($base_img_src, $thmb_width, $thmb_height, $thmb_fn))); + $full_img_src = join('&', array_filter(array($base_img_src, $thmb_fn))); + $frame_src = '?view=frame&eid=' .$row['EventId']. '&fid=' .$row['FrameId']; + + $row['Thumbnail'] = ''; } - return false; + $returned_rows[] = $row; + } # end foreach row matching search + + $data['rows'] = $returned_rows; + + # totalNotFiltered must equal total, except when either search bar has been used + $data['totalNotFiltered'] = count($unfiltered_rows); + if ( $search != '' || count($advsearch) ) { + $data['total'] = count($filtered_rows); + } else { + $data['total'] = $data['totalNotFiltered']; } - public static function is_valid_attr($attr) { - $attrs = array( - 'Score', - 'Delta', - 'TimeStamp', - 'Type', - 'FrameId', - 'EventId', - 'ExistsInFileSystem', - 'Emailed', - 'DiskSpace', - 'DiskPercent', - 'DiskBlocks', - 'MonitorName', - 'ServerId', - 'MonitorServerId', - 'StorageServerId', - 'FilterServerId', - 'DateTime', - 'Date', - 'Time', - 'Weekday', - 'StartDateTime', - 'FramesId', - 'FramesEventId', - 'StartDate', - 'StartTime', - 'StartWeekday', - 'EndDateTime', - 'EndDate', - 'EndTime', - 'EndWeekday', - 'Id', - 'Name', - 'MonitorId', - 'StorageId', - 'SecondaryStorageId', - 'Length', - 'Frames', - 'AlarmFrames', - 'TotScore', - 'AvgScore', - 'MaxScore', - 'Cause', - 'Notes', - 'StateId', - 'Archived' - ); - return in_array($attr, $attrs); - } -} # end class FilterTerm - -?> + return $data; +}