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 CWidgetFormSvgGraph extends CWidgetForm { 23 24 public function __construct($data, $templateid) { 25 parent::__construct($data, $templateid, WIDGET_SVG_GRAPH); 26 27 $this->data = self::convertDottedKeys($this->data); 28 29 /** 30 * Data set tab. 31 * 32 * Contains single CWidgetFieldGraphDataSet field for data sets definition and configuration. 33 */ 34 $field_ds = (new CWidgetFieldGraphDataSet('ds', _('Data set')))->setFlags(CWidgetField::FLAG_NOT_EMPTY); 35 36 if (array_key_exists('ds', $this->data)) { 37 $field_ds->setValue($this->data['ds']); 38 } 39 40 $this->fields[$field_ds->getName()] = $field_ds; 41 42 /** 43 * Display options tab. 44 * 45 * Used to select either data are loaded from History or Trends or turning automatic mode on. 46 */ 47 $field_data_source = (new CWidgetFieldRadioButtonList('source', _('History data selection'), [ 48 SVG_GRAPH_DATA_SOURCE_AUTO => _x('Auto', 'history source selection method'), 49 SVG_GRAPH_DATA_SOURCE_HISTORY => _('History'), 50 SVG_GRAPH_DATA_SOURCE_TRENDS => _('Trends') 51 ])) 52 ->setDefault(SVG_GRAPH_DATA_SOURCE_AUTO) 53 ->setModern(true); 54 55 if (array_key_exists('source', $this->data)) { 56 $field_data_source->setValue($this->data['source']); 57 } 58 59 $this->fields[$field_data_source->getName()] = $field_data_source; 60 61 /** 62 * Time period tab. 63 * 64 * Contains fields for specifying widget time options. 65 */ 66 // Checkbox to specify either relative dashboard time or widget's own time. 67 $field_graph_time = (new CWidgetFieldCheckBox('graph_time', _('Set custom time period'))) 68 ->setAction('jQuery("#time_from, #time_to, #time_from_calendar, #time_to_calendar")'. 69 '.prop("disabled", !jQuery(this).is(":checked"));' 70 ); 71 72 if (array_key_exists('graph_time', $this->data)) { 73 $field_graph_time->setValue($this->data['graph_time']); 74 } 75 76 $this->fields[$field_graph_time->getName()] = $field_graph_time; 77 78 // Date from. 79 $field_time_from = (new CWidgetFieldDatePicker('time_from', _('From')))->setDefault('now-1h'); 80 81 if ($field_graph_time->getValue() != SVG_GRAPH_CUSTOM_TIME) { 82 $field_time_from->setFlags(CWidgetField::FLAG_DISABLED); 83 } 84 elseif (array_key_exists('time_from', $this->data)) { 85 $field_time_from->setValue($this->data['time_from']); 86 } 87 88 $this->fields[$field_time_from->getName()] = $field_time_from; 89 90 // Time to. 91 $field_time_to = (new CWidgetFieldDatePicker('time_to', _('To')))->setDefault('now'); 92 93 if ($field_graph_time->getValue() != SVG_GRAPH_CUSTOM_TIME) { 94 $field_time_to->setFlags(CWidgetField::FLAG_DISABLED); 95 } 96 elseif (array_key_exists('time_to', $this->data)) { 97 $field_time_to->setValue($this->data['time_to']); 98 } 99 100 $this->fields[$field_time_to->getName()] = $field_time_to; 101 102 /** 103 * Axes tab. 104 * 105 * Contains fields to specify options for graph axes. 106 */ 107 // Show left Y axis. 108 $field_lefty = (new CWidgetFieldCheckBox('lefty', _('Left Y'), _('Show'))) 109 ->setDefault(SVG_GRAPH_AXIS_SHOW) 110 ->setAction('onLeftYChange()'); 111 112 if (array_key_exists('lefty', $this->data)) { 113 $field_lefty->setValue($this->data['lefty']); 114 } 115 116 $this->fields[$field_lefty->getName()] = $field_lefty; 117 118 // Min value on left Y axis. 119 $field_lefty_min = (new CWidgetFieldNumericBox('lefty_min', _('Min'))) 120 ->setPlaceholder(_('calculated')) 121 ->setFullName(_('Left Y').'/'._('Min')) 122 ->setWidth(ZBX_TEXTAREA_SMALL_WIDTH); 123 124 if ($field_lefty->getValue() != SVG_GRAPH_AXIS_SHOW) { 125 $field_lefty_min->setFlags(CWidgetField::FLAG_DISABLED); 126 } 127 elseif (array_key_exists('lefty_min', $this->data)) { 128 $field_lefty_min->setValue($this->data['lefty_min']); 129 } 130 131 $this->fields[$field_lefty_min->getName()] = $field_lefty_min; 132 133 // Max value on left Y axis. 134 $field_lefty_max = (new CWidgetFieldNumericBox('lefty_max', _('Max'))) 135 ->setPlaceholder(_('calculated')) 136 ->setFullName(_('Left Y').'/'._('Max')) 137 ->setWidth(ZBX_TEXTAREA_SMALL_WIDTH); 138 139 if ($field_lefty->getValue() != SVG_GRAPH_AXIS_SHOW) { 140 $field_lefty_max->setFlags(CWidgetField::FLAG_DISABLED); 141 } 142 elseif (array_key_exists('lefty_max', $this->data)) { 143 $field_lefty_max->setValue($this->data['lefty_max']); 144 } 145 146 $this->fields[$field_lefty_max->getName()] = $field_lefty_max; 147 148 // Specify the type of units on left Y axis. 149 $field_lefty_units = (new CWidgetFieldSelect('lefty_units', _('Units'), [ 150 SVG_GRAPH_AXIS_UNITS_AUTO => _x('Auto', 'history source selection method'), 151 SVG_GRAPH_AXIS_UNITS_STATIC => _x('Static', 'history source selection method') 152 ])) 153 ->setDefault(SVG_GRAPH_AXIS_UNITS_AUTO); 154 155 if ($field_lefty->getValue() != SVG_GRAPH_AXIS_SHOW) { 156 $field_lefty_units->setFlags(CWidgetField::FLAG_DISABLED); 157 } 158 elseif (array_key_exists('lefty_units', $this->data)) { 159 $field_lefty_units->setValue($this->data['lefty_units']); 160 } 161 162 $this->fields[$field_lefty_units->getName()] = $field_lefty_units; 163 164 // Static units on left Y axis. 165 $field_lefty_static_units = (new CWidgetFieldTextBox('lefty_static_units', null)) 166 ->setPlaceholder(_('value')) 167 ->setWidth(ZBX_TEXTAREA_TINY_WIDTH); 168 169 if ($field_lefty->getValue() != SVG_GRAPH_AXIS_SHOW 170 || $field_lefty_units->getValue() != SVG_GRAPH_AXIS_UNITS_STATIC) { 171 $field_lefty_static_units->setFlags(CWidgetField::FLAG_DISABLED); 172 } 173 elseif (array_key_exists('lefty_static_units', $this->data)) { 174 $field_lefty_static_units->setValue($this->data['lefty_static_units']); 175 } 176 177 $this->fields[$field_lefty_static_units->getName()] = $field_lefty_static_units; 178 179 // Show right Y axis. 180 $field_righty = (new CWidgetFieldCheckBox('righty', _('Right Y'), _('Show'))) 181 ->setDefault(SVG_GRAPH_AXIS_SHOW) 182 ->setAction('onRightYChange()'); 183 184 if (array_key_exists('righty', $this->data)) { 185 $field_righty->setValue($this->data['righty']); 186 } 187 188 $this->fields[$field_righty->getName()] = $field_righty; 189 190 // Min value on right Y axis. 191 $field_righty_min = (new CWidgetFieldNumericBox('righty_min', _('Min'))) 192 ->setPlaceholder(_('calculated')) 193 ->setFullName(_('Right Y').'/'._('Min')) 194 ->setWidth(ZBX_TEXTAREA_SMALL_WIDTH); 195 196 if ($field_righty->getValue() != SVG_GRAPH_AXIS_SHOW) { 197 $field_righty_min->setFlags(CWidgetField::FLAG_DISABLED); 198 } 199 elseif (array_key_exists('righty_min', $this->data)) { 200 $field_righty_min->setValue($this->data['righty_min']); 201 } 202 203 $this->fields[$field_righty_min->getName()] = $field_righty_min; 204 205 // Max value on right Y axis. 206 $field_righty_max = (new CWidgetFieldNumericBox('righty_max', _('Max'))) 207 ->setPlaceholder(_('calculated')) 208 ->setFullName(_('Right Y').'/'._('Max')) 209 ->setWidth(ZBX_TEXTAREA_SMALL_WIDTH); 210 211 if ($field_righty->getValue() != SVG_GRAPH_AXIS_SHOW) { 212 $field_righty_max->setFlags(CWidgetField::FLAG_DISABLED); 213 } 214 elseif (array_key_exists('righty_max', $this->data)) { 215 $field_righty_max->setValue($this->data['righty_max']); 216 } 217 218 $this->fields[$field_righty_max->getName()] = $field_righty_max; 219 220 // Specify the type of units on right Y axis. 221 $field_righty_units = (new CWidgetFieldSelect('righty_units', _('Units'), [ 222 SVG_GRAPH_AXIS_UNITS_AUTO => _x('Auto', 'history source selection method'), 223 SVG_GRAPH_AXIS_UNITS_STATIC => _x('Static', 'history source selection method') 224 ])) 225 ->setDefault(SVG_GRAPH_AXIS_UNITS_AUTO); 226 227 if ($field_righty->getValue() != SVG_GRAPH_AXIS_SHOW) { 228 $field_righty_units->setFlags(CWidgetField::FLAG_DISABLED); 229 } 230 elseif (array_key_exists('righty_units', $this->data)) { 231 $field_righty_units->setValue($this->data['righty_units']); 232 } 233 234 $this->fields[$field_righty_units->getName()] = $field_righty_units; 235 236 // Static units on right Y axis. 237 $field_righty_static_units = (new CWidgetFieldTextBox('righty_static_units', null)) 238 ->setPlaceholder(_('value')) 239 ->setWidth(ZBX_TEXTAREA_TINY_WIDTH); 240 241 if ($field_righty->getValue() != SVG_GRAPH_AXIS_SHOW 242 || $field_righty_units->getValue() != SVG_GRAPH_AXIS_UNITS_STATIC) { 243 $field_righty_static_units->setFlags(CWidgetField::FLAG_DISABLED); 244 } 245 elseif (array_key_exists('righty_static_units', $this->data)) { 246 $field_righty_static_units->setValue($this->data['righty_static_units']); 247 } 248 249 $this->fields[$field_righty_static_units->getName()] = $field_righty_static_units; 250 251 // Show X axis. 252 $field_axisx = (new CWidgetFieldCheckBox('axisx', _('X-Axis'), _('Show')))->setDefault(SVG_GRAPH_AXIS_SHOW); 253 254 if (array_key_exists('axisx', $this->data)) { 255 $field_axisx->setValue($this->data['axisx']); 256 } 257 258 $this->fields[$field_axisx->getName()] = $field_axisx; 259 260 /** 261 * Legend tab. 262 * 263 * Contains check-box field to show/hide legend and field to specify number of lines in which legend is shown. 264 */ 265 // Show legend. 266 $field_legend = (new CWidgetFieldCheckBox('legend', _('Show legend'))) 267 ->setAction('jQuery("[name=legend_lines]").rangeControl('. 268 'jQuery(this).is(":checked") ? "enable" : "disable"'. 269 ');') 270 ->setDefault(SVG_GRAPH_LEGEND_TYPE_SHORT); 271 272 if (array_key_exists('legend', $this->data)) { 273 $field_legend->setValue($this->data['legend']); 274 } 275 276 $this->fields[$field_legend->getName()] = $field_legend; 277 278 // Number of lines. 279 $field_legend_lines = (new CWidgetFieldRangeControl('legend_lines', _('Number of rows'), 280 SVG_GRAPH_LEGEND_LINES_MIN, SVG_GRAPH_LEGEND_LINES_MAX 281 )) 282 ->setDefault(SVG_GRAPH_LEGEND_LINES_MIN); 283 284 if ($field_legend->getValue() == SVG_GRAPH_LEGEND_TYPE_NONE) { 285 $field_legend_lines->setFlags(CWidgetField::FLAG_DISABLED); 286 } 287 if (array_key_exists('legend_lines', $this->data)) { 288 $field_legend_lines->setValue($this->data['legend_lines']); 289 } 290 291 $this->fields[$field_legend_lines->getName()] = $field_legend_lines; 292 293 /** 294 * Problems tab. 295 * 296 * Contains fields to configure highlighted problem areas in graph. 297 */ 298 // Checkbox: Selected items only. 299 $field_show_problems = (new CWidgetFieldCheckBox('show_problems', _('Show problems'))) 300 ->setAction( 301 'var on = jQuery(this).is(":checked"),'. 302 'widget = jQuery(this).closest(".ui-widget");'. 303 'jQuery("#graph_item_problems, #problem_name, #problemhosts_select")'. 304 '.prop("disabled", !on);'. 305 'jQuery("#problemhosts_").multiSelect(on ? "enable" : "disable");'. 306 'jQuery("[name^=\"severities[\"]", widget).prop("disabled", !on);'. 307 'jQuery("[name=\"evaltype\"]", widget).prop("disabled", !on);'. 308 'jQuery("input, button, z-select", jQuery("#tags_table_tags", widget)).prop("disabled", !on);' 309 ); 310 311 if (array_key_exists('show_problems', $this->data)) { 312 $field_show_problems->setValue($this->data['show_problems']); 313 } 314 315 $this->fields[$field_show_problems->getName()] = $field_show_problems; 316 317 // Checkbox: Selected items only. 318 $field_problems = (new CWidgetFieldCheckBox('graph_item_problems', _('Selected items only'))) 319 ->setDefault(SVG_GRAPH_SELECTED_ITEM_PROBLEMS); 320 321 if ($field_show_problems->getValue() != SVG_GRAPH_PROBLEMS_SHOW) { 322 $field_problems->setFlags(CWidgetField::FLAG_DISABLED); 323 } 324 elseif (array_key_exists('graph_item_problems', $this->data)) { 325 $field_problems->setValue($this->data['graph_item_problems']); 326 } 327 328 $this->fields[$field_problems->getName()] = $field_problems; 329 330 // Problem hosts. 331 $field_problemhosts = (new CWidgetFieldHostPatternSelect('problemhosts', _('Problem hosts'))) 332 ->setPlaceholder(_('host pattern')); 333 334 if ($field_show_problems->getValue() != SVG_GRAPH_PROBLEMS_SHOW) { 335 $field_problemhosts->setFlags(CWidgetField::FLAG_DISABLED); 336 } 337 elseif (array_key_exists('problemhosts', $this->data)) { 338 $field_problemhosts->setValue($this->data['problemhosts']); 339 } 340 341 $this->fields[$field_problemhosts->getName()] = $field_problemhosts; 342 343 // Severity checkboxes list. 344 $field_severities = new CWidgetFieldSeverities('severities', _('Severity')); 345 346 if ($field_show_problems->getValue() != SVG_GRAPH_PROBLEMS_SHOW) { 347 $field_severities->setFlags(CWidgetField::FLAG_DISABLED); 348 } 349 elseif (array_key_exists('severities', $this->data)) { 350 $field_severities->setValue($this->data['severities']); 351 } 352 353 $this->fields[$field_severities->getName()] = $field_severities; 354 355 // Problem name input-text field. 356 $field_problem_name = (new CWidgetFieldTextBox('problem_name', _('Problem'))) 357 ->setPlaceholder(_('problem pattern')); 358 359 if ($field_show_problems->getValue() != SVG_GRAPH_PROBLEMS_SHOW) { 360 $field_problem_name->setFlags(CWidgetField::FLAG_DISABLED); 361 } 362 elseif (array_key_exists('problem_name', $this->data)) { 363 $field_problem_name->setValue($this->data['problem_name']); 364 } 365 366 $this->fields[$field_problem_name->getName()] = $field_problem_name; 367 368 // Problem tag evaltype (And/Or). 369 $field_evaltype = (new CWidgetFieldRadioButtonList('evaltype', _('Tags'), [ 370 TAG_EVAL_TYPE_AND_OR => _('And/Or'), 371 TAG_EVAL_TYPE_OR => _('Or') 372 ])) 373 ->setDefault(TAG_EVAL_TYPE_AND_OR) 374 ->setModern(true); 375 376 if ($field_show_problems->getValue() != SVG_GRAPH_PROBLEMS_SHOW) { 377 $field_evaltype->setFlags(CWidgetField::FLAG_DISABLED); 378 } 379 elseif (array_key_exists('evaltype', $this->data)) { 380 $field_evaltype->setValue($this->data['evaltype']); 381 } 382 383 $this->fields[$field_evaltype->getName()] = $field_evaltype; 384 385 // Problem tags field. 386 $field_tags = new CWidgetFieldTags('tags', ''); 387 388 if ($field_show_problems->getValue() != SVG_GRAPH_PROBLEMS_SHOW) { 389 $field_tags->setFlags(CWidgetField::FLAG_DISABLED); 390 } 391 elseif (array_key_exists('tags', $this->data)) { 392 $field_tags->setValue($this->data['tags']); 393 } 394 395 $this->fields[$field_tags->getName()] = $field_tags; 396 397 /** 398 * Overrides tab. 399 * 400 * Contains single field for override configuration. 401 */ 402 $field_or = (new CWidgetFieldGraphOverride('or', _('Overrides')))->setFlags(CWidgetField::FLAG_NOT_EMPTY); 403 404 if (array_key_exists('or', $this->data)) { 405 $field_or->setValue($this->data['or']); 406 } 407 408 $this->fields[$field_or->getName()] = $field_or; 409 } 410 411 /** 412 * Validate "from" and "to" parameters for allowed period. 413 * 414 * @param string $from 415 * @param string $to 416 * 417 * @return array 418 */ 419 private static function validateTimeSelectorPeriod($from, $to) { 420 $errors = []; 421 $ts = []; 422 $ts['now'] = time(); 423 $range_time_parser = new CRangeTimeParser(); 424 425 foreach (['from' => $from, 'to' => $to] as $field => $value) { 426 $range_time_parser->parse($value); 427 $ts[$field] = $range_time_parser 428 ->getDateTime($field === 'from') 429 ->getTimestamp(); 430 } 431 432 $period = $ts['to'] - $ts['from'] + 1; 433 $range_time_parser->parse('now-'.CSettingsHelper::get(CSettingsHelper::MAX_PERIOD)); 434 $max_period = 1 + $ts['now'] - $range_time_parser 435 ->getDateTime(true) 436 ->getTimestamp(); 437 438 if ($period < ZBX_MIN_PERIOD) { 439 $errors[] = _n('Minimum time period to display is %1$s minute.', 440 'Minimum time period to display is %1$s minutes.', (int) (ZBX_MIN_PERIOD / SEC_PER_MIN) 441 ); 442 } 443 elseif ($period > $max_period) { 444 $errors[] = _n('Maximum time period to display is %1$s day.', 445 'Maximum time period to display is %1$s days.', (int) round($max_period / SEC_PER_DAY) 446 ); 447 } 448 449 return $errors; 450 } 451 452 /** 453 * Validate form fields. 454 * 455 * @param bool $strict Enables more strict validation of the form fields. 456 * Must be enabled for validation of input parameters in the widget configuration form. 457 * 458 * @return bool 459 */ 460 public function validate($strict = false) { 461 $errors = parent::validate($strict); 462 463 // Test graph custom time period. 464 if ($this->fields['graph_time']->getValue() == SVG_GRAPH_CUSTOM_TIME) { 465 $errors = array_merge($errors, self::validateTimeSelectorPeriod($this->fields['time_from']->getValue(), 466 $this->fields['time_to']->getValue() 467 )); 468 } 469 470 $number_parser = new CNumberParser(['with_suffix' => true]); 471 472 // Validate Min/Max values in Axes tab. 473 if ($this->fields['lefty']->getValue() == SVG_GRAPH_AXIS_SHOW) { 474 $lefty_min = $number_parser->parse($this->fields['lefty_min']->getValue()) == CParser::PARSE_SUCCESS 475 ? $number_parser->calcValue() 476 : ''; 477 478 $lefty_max = $number_parser->parse($this->fields['lefty_max']->getValue()) == CParser::PARSE_SUCCESS 479 ? $number_parser->calcValue() 480 : ''; 481 482 if ($lefty_min !== '' && $lefty_max !== '' && $lefty_min >= $lefty_max) { 483 $errors[] = _s('Invalid parameter "%1$s": %2$s.', _('Left Y').'/'._('Max'), 484 _('Y axis MAX value must be greater than Y axis MIN value') 485 ); 486 } 487 } 488 489 if ($this->fields['righty']->getValue() == SVG_GRAPH_AXIS_SHOW) { 490 $righty_min = $number_parser->parse($this->fields['righty_min']->getValue()) == CParser::PARSE_SUCCESS 491 ? $number_parser->calcValue() 492 : ''; 493 494 $righty_max = $number_parser->parse($this->fields['righty_max']->getValue()) == CParser::PARSE_SUCCESS 495 ? $number_parser->calcValue() 496 : ''; 497 498 if ($righty_min !== '' && $righty_max !== '' && $righty_min >= $righty_max) { 499 $errors[] = _s('Invalid parameter "%1$s": %2$s.', _('Right Y').'/'._('Max'), 500 _('Y axis MAX value must be greater than Y axis MIN value') 501 ); 502 } 503 } 504 505 return $errors; 506 } 507 508 /** 509 * Check if widget configuration is set to use overridden time. 510 * 511 * @param array $fields Widget configuration fields. 512 * @param int $fields['graph_time'] (optional) 513 * 514 * @return bool 515 */ 516 public static function hasOverrideTime($fields) { 517 return (array_key_exists('graph_time', $fields) && $fields['graph_time'] == SVG_GRAPH_CUSTOM_TIME); 518 } 519} 520