1<?php 2/* 3** Zabbix 4** Copyright (C) 2001-2021 Zabbix SIA 5** 6** This program is free software; you can redistribute it and/or modify 7** it under the terms of the GNU General Public License as published by 8** the Free Software Foundation; either version 2 of the License, or 9** (at your option) any later version. 10** 11** This program is distributed in the hope that it will be useful, 12** but WITHOUT ANY WARRANTY; without even the implied warranty of 13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14** GNU General Public License for more details. 15** 16** You should have received a copy of the GNU General Public License 17** along with this program; if not, write to the Free Software 18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19**/ 20 21 22class CFilter extends CDiv { 23 24 // Filter form object. 25 private $form; 26 // Filter form object name and id attribute. 27 private $name = 'zbx_filter'; 28 // Visibility of 'Apply', 'Reset' form buttons. Visibility is set to all tabs. 29 private $show_buttons = true; 30 31 /** 32 * Filter page URL. 33 * 34 * @var object 35 */ 36 private $url; 37 38 // Array of filter tab headers. Every header is mapped to it content via href(header) and id(content) attribute. 39 protected $headers = []; 40 // Array of filter tab content. 41 protected $tabs = []; 42 // jQuery.tabs initialization options. 43 protected $tabs_options = [ 44 'collapsible' => true, 45 'active' => false 46 ]; 47 // Profile data associated with filter object. 48 protected $idx = null; 49 protected $idx2 = 0; 50 51 /** 52 * List of predefined time ranges. 53 */ 54 protected $time_ranges = [ 55 [ 56 ['now-2d', 'now'], 57 ['now-7d', 'now'], 58 ['now-30d', 'now'], 59 ['now-3M', 'now'], 60 ['now-6M', 'now'], 61 ['now-1y', 'now'], 62 ['now-2y', 'now'] 63 ], 64 [ 65 ['now-1d/d', 'now-1d/d'], 66 ['now-2d/d', 'now-2d/d'], 67 ['now-1w/d', 'now-1w/d'], 68 ['now-1w/w', 'now-1w/w'], 69 ['now-1M/M', 'now-1M/M'], 70 ['now-1y/y', 'now-1y/y'] 71 ], 72 [ 73 ['now/d', 'now/d'], 74 ['now/d', 'now'], 75 ['now/w', 'now/w'], 76 ['now/w', 'now'], 77 ['now/M', 'now/M'], 78 ['now/M', 'now'], 79 ['now/y', 'now/y'], 80 ['now/y', 'now'] 81 ], 82 [ 83 ['now-5m', 'now'], 84 ['now-15m', 'now'], 85 ['now-30m', 'now'], 86 ['now-1h', 'now'], 87 ['now-3h', 'now'], 88 ['now-6h', 'now'], 89 ['now-12h', 'now'], 90 ['now-24h', 'now'] 91 ] 92 ]; 93 94 public function __construct(CUrl $url) { 95 parent::__construct(); 96 97 $this->url = $url; 98 99 $this 100 ->setAttribute('data-accessible', 1) 101 ->addClass('filter-space') 102 ->setId(uniqid('filter_')); 103 104 $this->form = (new CForm('get')) 105 ->cleanItems() 106 ->setAttribute('name', $this->name); 107 } 108 109 public function getName() { 110 return $this->name; 111 } 112 113 /** 114 * Add variable to filter form. 115 * 116 * @param string $name Variable name. 117 * @param string $value Variable value. 118 * 119 * @return CFilter 120 */ 121 public function addVar($name, $value) { 122 $this->form->addVar($name, $value); 123 124 return $this; 125 } 126 127 /** 128 * Hide filter tab buttons. Should be called before addFilterTab. 129 */ 130 public function hideFilterButtons() { 131 $this->show_buttons = false; 132 133 return $this; 134 } 135 136 /** 137 * Set profile 'idx' and 'idx2' data. 138 * 139 * @param string $idx Profile 'idx' string. 140 * @param int $idx2 Profile 'idx2' identifier, default 0. 141 * 142 * @return CFilter 143 */ 144 public function setProfile($idx, $idx2 = 0) { 145 $this->idx = $idx; 146 $this->idx2 = $idx2; 147 148 $this->setAttribute('data-profile-idx', $idx); 149 $this->setAttribute('data-profile-idx2', $idx2); 150 151 return $this; 152 } 153 154 /** 155 * Adds an item inside the form object. 156 * 157 * @param mixed $item An item to add inside the form object. 158 * 159 * @return CFilter 160 */ 161 public function addFormItem($item) { 162 $this->form->addItem($item); 163 164 return $this; 165 } 166 167 /** 168 * Set active tab. 169 * 170 * @param int $tab 1 based index of active tab. If set to 0 all tabs will be collapsed. 171 * 172 * @return CFilter 173 */ 174 public function setActiveTab($tab) { 175 $this->tabs_options['active'] = $tab > 0 ? $tab - 1 : false; 176 177 return $this; 178 } 179 180 /** 181 * Add tab with filter form. 182 * 183 * @param string $header Tab header title string. 184 * @param array $columns Array of filter columns markup. 185 * @param array $footer Additional markup objects for filter tab, default null. 186 * 187 * @return CFilter 188 */ 189 public function addFilterTab($header, $columns, $footer = null) { 190 $row = (new CDiv())->addClass(ZBX_STYLE_ROW); 191 $body = []; 192 $anchor = 'tab_'.count($this->tabs); 193 194 foreach ($columns as $column) { 195 $row->addItem((new CDiv($column))->addClass(ZBX_STYLE_CELL)); 196 } 197 198 $body[] = (new CDiv()) 199 ->addClass(ZBX_STYLE_TABLE) 200 ->addClass(ZBX_STYLE_FILTER_FORMS) 201 ->addItem($row); 202 203 if ($this->show_buttons) { 204 $body[] = (new CDiv()) 205 ->addClass(ZBX_STYLE_FILTER_FORMS) 206 ->addItem( 207 (new CSubmitButton(_('Apply'), 'filter_set', 1)) 208 ->onClick('javascript: chkbxRange.clearSelectedOnFilterChange();') 209 ) 210 ->addItem( 211 (new CRedirectButton(_('Reset'), 212 $this->url 213 ->setArgument('filter_rst', 1) 214 ->getUrl() 215 )) 216 ->addClass(ZBX_STYLE_BTN_ALT) 217 ->onClick('javascript: chkbxRange.clearSelectedOnFilterChange();') 218 ); 219 } 220 221 if ($footer !== null) { 222 $body[] = $footer; 223 } 224 225 return $this->addTab( 226 (new CLink($header, '#'.$anchor))->addClass(ZBX_STYLE_FILTER_TRIGGER), 227 (new CDiv($body)) 228 ->addClass(ZBX_STYLE_FILTER_CONTAINER) 229 ->setId($anchor) 230 ); 231 } 232 233 /** 234 * Add time selector specific tab. Should be called before any tab is added. Adds two tabs: 235 * - time selector range change buttons: back, zoom out, forward. 236 * - time selector range change form with predefined ranges. 237 * 238 * @param string $from Start date. (can be in relative time format, example: now-1w) 239 * @param string $to End date. (can be in relative time format, example: now-1w) 240 * @param bool $visible Either to make time selector visible or hidden. 241 * @param string $format Date and time format used in CDateSelector. 242 * 243 * @return CFilter 244 */ 245 public function addTimeSelector($from, $to, $visible = true, $format = ZBX_FULL_DATE_TIME) { 246 $header = relativeDateToText($from, $to); 247 248 if ($visible) { 249 $this->addTab(new CDiv([ 250 (new CSimpleButton())->addClass(ZBX_STYLE_BTN_TIME_LEFT), 251 (new CSimpleButton(_('Zoom out')))->addClass(ZBX_STYLE_BTN_TIME_OUT), 252 (new CSimpleButton())->addClass(ZBX_STYLE_BTN_TIME_RIGHT) 253 ]), null); 254 255 $predefined_ranges = []; 256 257 foreach ($this->time_ranges as $column_ranges) { 258 $column = (new CList())->addClass(ZBX_STYLE_TIME_QUICK); 259 260 foreach ($column_ranges as $range) { 261 $label = relativeDateToText($range[0], $range[1]); 262 $is_selected = ($header === $label); 263 264 $column->addItem((new CLink($label)) 265 ->setAttribute('data-from', $range[0]) 266 ->setAttribute('data-to', $range[1]) 267 ->setAttribute('data-label', $label) 268 ->addClass($is_selected ? ZBX_STYLE_SELECTED : null) 269 ); 270 } 271 272 $predefined_ranges[] = (new CDiv($column))->addClass(ZBX_STYLE_CELL); 273 } 274 275 $anchor = 'tab_'.count($this->tabs); 276 277 $this->addTab( 278 (new CLink($header, '#'.$anchor))->addClass(ZBX_STYLE_BTN_TIME), 279 (new CDiv([ 280 (new CDiv([ 281 new CList([ 282 new CLabel(_('From'), 'from'), 283 (new CDateSelector('from', $from))->setDateFormat($format) 284 ]), 285 (new CList([(new CListItem(''))->addClass(ZBX_STYLE_RED)])) 286 ->setAttribute('data-error-for', 'from') 287 ->addClass(ZBX_STYLE_TIME_INPUT_ERROR) 288 ->addStyle('display: none'), 289 new CList([ 290 new CLabel(_('To'), 'to'), 291 (new CDateSelector('to', $to))->setDateFormat($format) 292 ]), 293 (new CList([(new CListItem(''))->addClass(ZBX_STYLE_RED)])) 294 ->setAttribute('data-error-for', 'to') 295 ->addClass(ZBX_STYLE_TIME_INPUT_ERROR) 296 ->addStyle('display: none'), 297 new CList([ 298 new CButton('apply', _('Apply')) 299 ]) 300 ]))->addClass(ZBX_STYLE_TIME_INPUT), 301 (new CDiv($predefined_ranges))->addClass(ZBX_STYLE_TIME_QUICK_RANGE) 302 ])) 303 ->addClass(ZBX_STYLE_FILTER_CONTAINER) 304 ->addClass(ZBX_STYLE_TIME_SELECTION_CONTAINER) 305 ->setId($anchor) 306 ); 307 } 308 else { 309 $this 310 ->setAttribute('data-accessible', 0) 311 ->addTab(null, (new CDiv([ 312 new CVar('from', $from), 313 new CVar('to', $to) 314 ]))); 315 } 316 317 return $this; 318 } 319 320 /** 321 * Add tab. 322 * 323 * @param string|CTag $header Tab header title string or CTag container. 324 * @param array $body Array of body elements. 325 * 326 * @return CFilter 327 */ 328 public function addTab($header, $body) { 329 $this->headers[] = $header; 330 $this->tabs[] = $body; 331 332 return $this; 333 } 334 335 /** 336 * Return javascript code for jquery-ui initialization. 337 * 338 * @return string 339 */ 340 private function getJS() { 341 $id = '#'.$this->getId(); 342 343 $js = 'jQuery("'.$id.'").tabs('.json_encode($this->tabs_options).').show();'; 344 345 // Set the focus to a field with autofocus after the filter becomes visible. 346 $js .= 'jQuery("[autofocus=autofocus]", jQuery("'.$id.'")).filter(":visible").focus();'; 347 348 if ($this->idx !== null && $this->idx !== '') { 349 $js .= 'jQuery("'.$id.'").on("tabsactivate", function(e, ui) {'. 350 'var active = ui.newPanel.length ? jQuery(this).tabs("option", "active") + 1 : 0;'. 351 'updateUserProfile("'.$this->idx.'.active", active, []);'. 352 353 'if (active) {'. 354 'jQuery("[autofocus=autofocus]", ui.newPanel).focus();'. 355 '}'. 356 '});'; 357 } 358 359 return $js; 360 } 361 362 /** 363 * Render current CFilter object as HTML string. 364 * 365 * @return string 366 */ 367 public function toString($destroy = true) { 368 $headers = (new CList())->addClass(ZBX_STYLE_FILTER_BTN_CONTAINER); 369 $headers_cnt = 0; 370 371 if ($this->tabs_options['active'] !== false 372 && !array_key_exists($this->tabs_options['active'], $this->headers)) { 373 $this->tabs_options['active'] = 0; 374 } 375 376 foreach ($this->headers as $index => $header) { 377 if ($header) { 378 $headers->addItem($header); 379 $headers_cnt++; 380 } 381 382 if ($this->tabs[$index] !== null && $index !== $this->tabs_options['active']) { 383 $this->tabs[$index]->addStyle('display: none'); 384 } 385 } 386 387 $this 388 ->addStyle('display:none') 389 ->form->addItem($this->tabs); 390 391 if ($headers_cnt) { 392 $this 393 ->addItem($headers) 394 ->setAttribute('aria-label', _('Filter')); 395 } 396 397 $this->addItem($this->form); 398 399 return parent::toString($destroy).($headers_cnt ? get_js($this->getJS()) : ''); 400 } 401} 402