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 CWidgetHelper { 23 24 /** 25 * Create CForm for widget configuration form. 26 * 27 * @return CForm 28 */ 29 public static function createForm() { 30 return (new CForm('post')) 31 ->cleanItems() 32 ->setId('widget-dialogue-form') 33 ->setName('widget_dialogue_form'); 34 } 35 36 /** 37 * Create CFormList for widget configuration form with default fields in it. 38 * 39 * @param string $name 40 * @param string $type 41 * @param int $view_mode ZBX_WIDGET_VIEW_MODE_NORMAL | ZBX_WIDGET_VIEW_MODE_HIDDEN_HEADER 42 * @param array $known_widget_types 43 * @param CWidgetFieldSelect|null $field_rf_rate 44 * 45 * @return CFormList 46 */ 47 public static function createFormList($name, $type, $view_mode, $known_widget_types, $field_rf_rate) { 48 $form_list = (new CFormList()) 49 ->addItem((new CListItem([ 50 (new CDiv(new CLabel(_('Type'), 'label-type')))->addClass(ZBX_STYLE_TABLE_FORMS_TD_LEFT), 51 (new CDiv([ 52 (new CDiv((new CCheckBox('show_header')) 53 ->setLabel(_('Show header')) 54 ->setLabelPosition(CCheckBox::LABEL_POSITION_LEFT) 55 ->setId('show_header') 56 ->setChecked($view_mode == ZBX_WIDGET_VIEW_MODE_NORMAL) 57 ))->addClass(ZBX_STYLE_TABLE_FORMS_SECOND_COLUMN), 58 (new CSelect('type')) 59 ->setFocusableElementId('label-type') 60 ->setId('type') 61 ->setValue($type) 62 ->setAttribute('autofocus', 'autofocus') 63 ->addOptions(CSelect::createOptionsFromArray($known_widget_types)) 64 ]))->addClass(ZBX_STYLE_TABLE_FORMS_TD_RIGHT) 65 ]))->addClass('table-forms-row-with-second-field') 66 ) 67 ->addRow(_('Name'), 68 (new CTextBox('name', $name)) 69 ->setAttribute('placeholder', _('default')) 70 ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH) 71 ) 72 ->addItem( 73 (new CScriptTag(' 74 $("z-select#type").on("change", () => ZABBIX.Dashboard.reloadWidgetProperties()); 75 76 document 77 .getElementById("widget-dialogue-form") 78 .addEventListener("change", (e) => { 79 const is_trimmable = e.target.matches( 80 \'input[type="text"]:not([data-no-trim="1"]), textarea:not([data-no-trim="1"])\' 81 ); 82 83 if (is_trimmable) { 84 e.target.value = e.target.value.trim(); 85 } 86 }, {capture: true}); 87 '))->setOnDocumentReady() 88 ); 89 90 if ($field_rf_rate !== null) { 91 $form_list->addRow(self::getLabel($field_rf_rate), self::getSelect($field_rf_rate)); 92 } 93 94 return $form_list; 95 } 96 97 /** 98 * Add Columns and Rows fields to the form of iterator. 99 * 100 * @param CFormList $form_list 101 * @param CWidgetFieldIntegerBox $field_columns 102 * @param CWidgetFieldIntegerBox $field_rows 103 */ 104 public static function addIteratorFields($form_list, $field_columns, $field_rows) { 105 $form_list 106 ->addRow(self::getLabel($field_columns), self::getIntegerBox($field_columns)) 107 ->addRow(self::getLabel($field_rows), self::getIntegerBox($field_rows)); 108 } 109 110 /** 111 * Creates label linked to the field. 112 * 113 * @param CWidgetField $field 114 * 115 * @return CLabel 116 */ 117 public static function getLabel($field) { 118 if ($field instanceof CWidgetFieldSelect) { 119 return (new CLabel($field->getLabel(), 'label-'.$field->getName())) 120 ->setAsteriskMark(self::isAriaRequired($field)); 121 } 122 123 return (new CLabel($field->getLabel(), $field->getName())) 124 ->setAsteriskMark(self::isAriaRequired($field)); 125 } 126 127 /** 128 * @param CWidgetFieldSelect $field 129 * 130 * @return CSelect 131 */ 132 public static function getSelect($field) { 133 return (new CSelect($field->getName())) 134 ->setId($field->getName()) 135 ->setFocusableElementId('label-'.$field->getName()) 136 ->setValue($field->getValue()) 137 ->addOptions(CSelect::createOptionsFromArray($field->getValues())) 138 ->setDisabled($field->getFlags() & CWidgetField::FLAG_DISABLED) 139 ->setAriaRequired(self::isAriaRequired($field)); 140 } 141 142 /** 143 * @param CWidgetFieldTextBox $field 144 * 145 * @return CTextBox 146 */ 147 public static function getTextBox($field) { 148 return (new CTextBox($field->getName(), $field->getValue())) 149 ->setAriaRequired(self::isAriaRequired($field)) 150 ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED)) 151 ->setAttribute('placeholder', $field->getPlaceholder()) 152 ->setWidth($field->getWidth()); 153 } 154 155 /** 156 * @param CWidgetFieldUrl $field 157 * 158 * @return CTextBox 159 */ 160 public static function getUrlBox($field) { 161 return (new CTextBox($field->getName(), $field->getValue())) 162 ->setAriaRequired(self::isAriaRequired($field)) 163 ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH); 164 } 165 166 /** 167 * @param CWidgetFieldRangeControl $field 168 * 169 * @return CRangeControl 170 */ 171 public static function getRangeControl($field) { 172 return (new CRangeControl($field->getName(), (int) $field->getValue())) 173 ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED)) 174 ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH) 175 ->setStep($field->getStep()) 176 ->setMin($field->getMin()) 177 ->setMax($field->getMax()); 178 } 179 180 /** 181 * @param CWidgetFieldHostPatternSelect $field Widget field object. 182 * @param string $form_name HTML form element name. 183 * 184 * @return CDiv 185 */ 186 public static function getHostPatternSelect($field, $form_name) { 187 return (new CPatternSelect([ 188 'name' => $field->getName().'[]', 189 'object_name' => 'hosts', 190 'data' => $field->getValue(), 191 'placeholder' => $field->getPlaceholder(), 192 'popup' => [ 193 'parameters' => [ 194 'srctbl' => 'hosts', 195 'srcfld1' => 'hostid', 196 'dstfrm' => $form_name, 197 'dstfld1' => zbx_formatDomId($field->getName().'[]') 198 ] 199 ], 200 'add_post_js' => false 201 ])) 202 ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED)) 203 ->setAriaRequired(self::isAriaRequired($field)) 204 ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH); 205 } 206 207 /** 208 * @param CWidgetFieldCheckBox $field 209 * 210 * @return array 211 */ 212 public static function getCheckBox($field) { 213 return [(new CVar($field->getName(), '0'))->removeId(), (new CCheckBox($field->getName())) 214 ->setChecked((bool) $field->getValue()) 215 ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED)) 216 ->setLabel($field->getCaption()) 217 ->onChange($field->getAction()) 218 ]; 219 } 220 221 /** 222 * Creates label linked to the multiselect field. 223 * 224 * @param CWidgetFieldMs $field 225 * 226 * @return CLabel 227 */ 228 public static function getMultiselectLabel($field) { 229 $field_name = $field->getName(); 230 231 if ($field instanceof CWidgetFieldMs) { 232 $field_name .= ($field->isMultiple() ? '[]' : ''); 233 } 234 else { 235 $field_name .= '[]'; 236 } 237 238 return (new CLabel($field->getLabel(), $field_name.'_ms')) 239 ->setAsteriskMark(self::isAriaRequired($field)); 240 } 241 242 /** 243 * @param CWidgetFieldMs $field 244 * @param array $captions 245 * @param string $form_name 246 * 247 * @return CMultiSelect 248 */ 249 private static function getMultiselectField($field, $captions, $form_name, $object_name, $popup_options) { 250 $field_name = $field->getName().($field->isMultiple() ? '[]' : ''); 251 $options = [ 252 'name' => $field_name, 253 'object_name' => $object_name, 254 'multiple' => $field->isMultiple(), 255 'data' => $captions, 256 'popup' => [ 257 'parameters' => [ 258 'dstfrm' => $form_name, 259 'dstfld1' => zbx_formatDomId($field_name) 260 ] + $popup_options 261 ], 262 'add_post_js' => false 263 ]; 264 265 if ($field instanceof CWidgetFieldMsHost && $field->filter_preselect_host_group_field) { 266 $options['popup']['filter_preselect_fields']['hostgroups'] = $field->filter_preselect_host_group_field; 267 } 268 269 return (new CMultiSelect($options)) 270 ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH) 271 ->setAriaRequired(self::isAriaRequired($field)); 272 } 273 274 /** 275 * @param CWidgetFieldMsGroup $field 276 * @param array $captions 277 * @param string $form_name 278 * 279 * @return CMultiSelect 280 */ 281 public static function getGroup($field, $captions, $form_name) { 282 return self::getMultiselectField($field, $captions, $form_name, 'hostGroup', [ 283 'srctbl' => 'host_groups', 284 'srcfld1' => 'groupid', 285 'real_hosts' => true, 286 'enrich_parent_groups' => true 287 ] + $field->getFilterParameters()); 288 } 289 290 /** 291 * @param CWidgetFieldMsHost $field 292 * @param array $captions 293 * @param string $form_name 294 * 295 * @return CMultiSelect 296 */ 297 public static function getHost($field, $captions, $form_name) { 298 return self::getMultiselectField($field, $captions, $form_name, 'hosts', [ 299 'srctbl' => 'hosts', 300 'srcfld1' => 'hostid' 301 ] + $field->getFilterParameters()); 302 } 303 304 /** 305 * @param CWidgetFieldMsItem $field 306 * @param array $captions 307 * @param string $form_name 308 * 309 * @return CMultiSelect 310 */ 311 public static function getItem($field, $captions, $form_name) { 312 return self::getMultiselectField($field, $captions, $form_name, 'items', [ 313 'srctbl' => 'items', 314 'srcfld1' => 'itemid', 315 'webitems' => true 316 ] + $field->getFilterParameters()); 317 } 318 319 /** 320 * @param CWidgetFieldMsGraph $field 321 * @param array $captions 322 * @param string $form_name 323 * 324 * @return CMultiSelect 325 */ 326 public static function getGraph($field, $captions, $form_name) { 327 return self::getMultiselectField($field, $captions, $form_name, 'graphs', [ 328 'srctbl' => 'graphs', 329 'srcfld1' => 'graphid', 330 'srcfld2' => 'name', 331 'with_graphs' => true 332 ] + $field->getFilterParameters()); 333 } 334 335 /** 336 * @param CWidgetFieldMsItemPrototype $field 337 * @param array $captions 338 * @param string $form_name 339 * 340 * @return CMultiSelect 341 */ 342 public static function getItemPrototype($field, $captions, $form_name) { 343 return self::getMultiselectField($field, $captions, $form_name, 'item_prototypes', [ 344 'srctbl' => 'item_prototypes', 345 'srcfld1' => 'itemid' 346 ] + $field->getFilterParameters()); 347 } 348 349 /** 350 * @param CWidgetFieldMsGraphPrototype $field 351 * @param array $captions 352 * @param string $form_name 353 * 354 * @return CMultiSelect 355 */ 356 public static function getGraphPrototype($field, $captions, $form_name) { 357 return self::getMultiselectField($field, $captions, $form_name, 'graph_prototypes', [ 358 'srctbl' => 'graph_prototypes', 359 'srcfld1' => 'graphid', 360 'srcfld2' => 'name', 361 'with_graph_prototypes' => true 362 ] + $field->getFilterParameters()); 363 } 364 365 public static function getSelectResource($field, $caption, $form_name) { 366 return [ 367 (new CTextBox($field->getName().'_caption', $caption, true)) 368 ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH) 369 ->setAriaRequired(self::isAriaRequired($field)), 370 (new CDiv())->addClass(ZBX_STYLE_FORM_INPUT_MARGIN), 371 (new CButton('select', _('Select'))) 372 ->addClass(ZBX_STYLE_BTN_GREY) 373 ->onClick('return PopUp("popup.generic",'. 374 json_encode($field->getPopupOptions($form_name)).', null, this);') 375 ]; 376 } 377 378 /** 379 * Creates select field without values, to later fill it by JS script. 380 * 381 * @param CWidgetFieldWidgetSelect $field 382 * 383 * @return CSelect 384 */ 385 public static function getEmptySelect($field) { 386 return (new CSelect($field->getName())) 387 ->setFocusableElementId('label-'.$field->getName()) 388 ->setId($field->getName()) 389 ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH) 390 ->setAriaRequired(self::isAriaRequired($field)); 391 } 392 393 /** 394 * @param CWidgetFieldIntegerBox $field 395 * 396 * @return CNumericBox 397 */ 398 public static function getIntegerBox($field) { 399 return (new CNumericBox($field->getName(), $field->getValue(), $field->getMaxLength())) 400 ->setWidth(ZBX_TEXTAREA_NUMERIC_STANDARD_WIDTH) 401 ->setAriaRequired(self::isAriaRequired($field)); 402 } 403 404 /** 405 * @param CWidgetFieldNumericBox $field 406 * 407 * @return CTextBox 408 */ 409 public static function getNumericBox($field) { 410 return (new CTextBox($field->getName(), $field->getValue())) 411 ->setAriaRequired(self::isAriaRequired($field)) 412 ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED)) 413 ->setAttribute('placeholder', $field->getPlaceholder()) 414 ->setWidth($field->getWidth()); 415 } 416 417 /** 418 * @param CWidgetFieldRadioButtonList $field 419 * 420 * @return CRadioButtonList 421 */ 422 public static function getRadioButtonList($field) { 423 $radio_button_list = (new CRadioButtonList($field->getName(), $field->getValue())) 424 ->setModern($field->getModern()) 425 ->setAriaRequired(self::isAriaRequired($field)); 426 427 foreach ($field->getValues() as $key => $value) { 428 $radio_button_list 429 ->addValue($value, $key, null, $field->getAction()) 430 ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED)); 431 } 432 433 return $radio_button_list; 434 } 435 436 /** 437 * @param CWidgetFieldSeverities $field 438 * 439 * @return CSeverityCheckBoxList 440 */ 441 public static function getSeverities($field) { 442 return (new CSeverityCheckBoxList($field->getName())) 443 ->setChecked($field->getValue()) 444 ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED)) 445 ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH); 446 } 447 448 /** 449 * @param CWidgetFieldCheckBoxList $field 450 * @param array $list Option list array. 451 * 452 * @return CList 453 */ 454 public static function getCheckBoxList($field, array $list) { 455 $checkbox_list = (new CList())->addClass(ZBX_STYLE_LIST_CHECK_RADIO); 456 457 foreach ($list as $key => $label) { 458 $checkbox_list->addItem( 459 (new CCheckBox($field->getName().'[]', $key)) 460 ->setLabel($label) 461 ->setId($field->getName().'_'.$key) 462 ->setChecked(in_array($key, $field->getValue())) 463 ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED)) 464 ); 465 } 466 467 return $checkbox_list; 468 } 469 470 /** 471 * @param CWidgetFieldTags $field 472 * 473 * @return CTable 474 */ 475 public static function getTags($field) { 476 $tags = $field->getValue(); 477 478 if (!$tags) { 479 $tags = [['tag' => '', 'operator' => TAG_OPERATOR_LIKE, 'value' => '']]; 480 } 481 482 $tags_table = (new CTable())->setId('tags_table_'.$field->getName()); 483 $enabled = !($field->getFlags() & CWidgetField::FLAG_DISABLED); 484 $i = 0; 485 486 foreach ($tags as $tag) { 487 $zselect_operator = (new CSelect($field->getName().'['.$i.'][operator]')) 488 ->addOptions(CSelect::createOptionsFromArray([ 489 TAG_OPERATOR_EXISTS => _('Exists'), 490 TAG_OPERATOR_EQUAL => _('Equals'), 491 TAG_OPERATOR_LIKE => _('Contains'), 492 TAG_OPERATOR_NOT_EXISTS => _('Does not exist'), 493 TAG_OPERATOR_NOT_EQUAL => _('Does not equal'), 494 TAG_OPERATOR_NOT_LIKE => _('Does not contain') 495 ])) 496 ->setValue($tag['operator']) 497 ->setFocusableElementId($field->getName().'-'.$i.'-operator-select') 498 ->setId($field->getName().'_'.$i.'_operator'); 499 500 if (!$enabled) { 501 $zselect_operator->setDisabled(); 502 } 503 504 $tags_table->addRow([ 505 (new CTextBox($field->getName().'['.$i.'][tag]', $tag['tag'])) 506 ->setAttribute('placeholder', _('tag')) 507 ->setWidth(ZBX_TEXTAREA_FILTER_SMALL_WIDTH) 508 ->setAriaRequired(self::isAriaRequired($field)) 509 ->setEnabled($enabled), 510 $zselect_operator, 511 (new CTextBox($field->getName().'['.$i.'][value]', $tag['value'])) 512 ->setAttribute('placeholder', _('value')) 513 ->setWidth(ZBX_TEXTAREA_FILTER_SMALL_WIDTH) 514 ->setAriaRequired(self::isAriaRequired($field)) 515 ->setId($field->getName().'_'.$i.'_value') 516 ->setEnabled($enabled), 517 (new CCol( 518 (new CButton($field->getName().'['.$i.'][remove]', _('Remove'))) 519 ->addClass(ZBX_STYLE_BTN_LINK) 520 ->addClass('element-table-remove') 521 ->setEnabled($enabled) 522 ))->addClass(ZBX_STYLE_NOWRAP) 523 ], 'form_row'); 524 525 $i++; 526 } 527 528 $tags_table->addRow( 529 (new CCol( 530 (new CButton('tags_add', _('Add'))) 531 ->addClass(ZBX_STYLE_BTN_LINK) 532 ->addClass('element-table-add') 533 ->setEnabled($enabled) 534 ))->setColSpan(3) 535 ); 536 537 return $tags_table; 538 } 539 540 /** 541 * JS Template for one tag line for Tags field 542 * 543 * @param CWidgetFieldTags $field 544 * 545 * @return string 546 */ 547 public static function getTagsTemplate($field) { 548 return (new CRow([ 549 (new CTextBox($field->getName().'[#{rowNum}][tag]')) 550 ->setAttribute('placeholder', _('tag')) 551 ->setWidth(ZBX_TEXTAREA_FILTER_SMALL_WIDTH) 552 ->setAriaRequired(self::isAriaRequired($field)), 553 (new CSelect($field->getName().'[#{rowNum}][operator]')) 554 ->addOptions(CSelect::createOptionsFromArray([ 555 TAG_OPERATOR_EXISTS => _('Exists'), 556 TAG_OPERATOR_EQUAL => _('Equals'), 557 TAG_OPERATOR_LIKE => _('Contains'), 558 TAG_OPERATOR_NOT_EXISTS => _('Does not exist'), 559 TAG_OPERATOR_NOT_EQUAL => _('Does not equal'), 560 TAG_OPERATOR_NOT_LIKE => _('Does not contain') 561 ])) 562 ->setValue(TAG_OPERATOR_LIKE) 563 ->setFocusableElementId($field->getName().'-#{rowNum}-operator-select') 564 ->setId($field->getName().'_#{rowNum}_operator'), 565 (new CTextBox($field->getName().'[#{rowNum}][value]')) 566 ->setAttribute('placeholder', _('value')) 567 ->setWidth(ZBX_TEXTAREA_FILTER_SMALL_WIDTH) 568 ->setAriaRequired(self::isAriaRequired($field)) 569 ->setId($field->getName().'_#{rowNum}_value'), 570 (new CCol( 571 (new CButton($field->getName().'[#{rowNum}][remove]', _('Remove'))) 572 ->addClass(ZBX_STYLE_BTN_LINK) 573 ->addClass('element-table-remove') 574 ))->addClass(ZBX_STYLE_NOWRAP) 575 ])) 576 ->addClass('form_row') 577 ->toString(); 578 } 579 580 /** 581 * @param CWidgetFieldDatePicker $field 582 * 583 * @return CDateSelector 584 */ 585 public static function getDatePicker($field) { 586 return (new CDateSelector($field->getName(), $field->getValue())) 587 ->setAriaRequired(self::isAriaRequired($field)) 588 ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED)); 589 } 590 591 /** 592 * Function returns array containing HTML objects filled with given values. Used to generate HTML in widget 593 * overrides field. 594 * 595 * @param CWidgetFieldGraphOverride $field 596 * @param array $value Values to fill in particular data set row. See self::setValue() for 597 * detailed description. 598 * @param string $form_name Name of form in which data set fields resides. 599 * @param int|string $row_num Unique data set numeric identifier. Used to make unique field names. 600 * 601 * @return array 602 */ 603 public static function getGraphOverrideLayout($field, array $value, $form_name, $row_num) { 604 $inputs = []; 605 606 // Create override optins list. 607 foreach (CWidgetFieldGraphOverride::getOverrideOptions() as $option) { 608 if (array_key_exists($option, $value)) { 609 $inputs[] = (new CVar($field->getName().'['.$row_num.']['.$option.']', $value[$option])); 610 } 611 } 612 613 return (new CListItem([ 614 /** 615 * First line: host pattern field, item pattern field. 616 * Contains also drag and drop button and delete button. 617 */ 618 (new CDiv([ 619 (new CDiv()) 620 ->addClass(ZBX_STYLE_DRAG_ICON) 621 ->addStyle('position: absolute; margin-left: -25px;'), 622 (new CDiv([ 623 (new CDiv( 624 (new CPatternSelect([ 625 'name' => $field->getName().'['.$row_num.'][hosts][]', 626 'object_name' => 'hosts', 627 'data' => $value['hosts'], 628 'placeholder' => _('host pattern'), 629 'popup' => [ 630 'parameters' => [ 631 'srctbl' => 'hosts', 632 'srcfld1' => 'hostid', 633 'dstfrm' => $form_name, 634 'dstfld1' => zbx_formatDomId($field->getName().'['.$row_num.'][hosts][]') 635 ] 636 ], 637 'add_post_js' => false 638 ])) 639 ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED)) 640 ->setAriaRequired(self::isAriaRequired($field)) 641 ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH) 642 ))->addClass(ZBX_STYLE_COLUMN_50), 643 (new CDiv( 644 (new CPatternSelect([ 645 'name' => $field->getName().'['.$row_num.'][items][]', 646 'object_name' => 'items', 647 'data' => $value['items'], 648 'placeholder' => _('item pattern'), 649 'multiple' => true, 650 'popup' => [ 651 'parameters' => [ 652 'srctbl' => 'items', 653 'srcfld1' => 'itemid', 654 'real_hosts' => 1, 655 'numeric' => 1, 656 'webitems' => 1, 657 'orig_names' => 1, 658 'dstfrm' => $form_name, 659 'dstfld1' => zbx_formatDomId($field->getName().'['.$row_num.'][items][]') 660 ] 661 ], 662 'add_post_js' => false 663 ])) 664 ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED)) 665 ->setAriaRequired(self::isAriaRequired($field)) 666 ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH) 667 ))->addClass(ZBX_STYLE_COLUMN_50) 668 ])) 669 ->addClass(ZBX_STYLE_COLUMNS) 670 ->addClass(ZBX_STYLE_COLUMNS_NOWRAP) 671 ->addClass(ZBX_STYLE_COLUMN_95), 672 673 (new CDiv( 674 (new CButton()) 675 ->setAttribute('title', _('Delete')) 676 ->addClass(ZBX_STYLE_REMOVE_BTN) 677 ->removeId() 678 )) 679 ->addClass(ZBX_STYLE_COLUMN_5) 680 ])) 681 ->addClass(ZBX_STYLE_COLUMNS), 682 683 // Selected override options. 684 (new CList($inputs)) 685 ->addClass(ZBX_STYLE_OVERRIDES_OPTIONS_LIST) 686 ->addItem((new CButton(null, (new CSpan()) 687 ->addClass(ZBX_STYLE_PLUS_ICON) 688 ->addStyle('margin-right: 0px;') 689 )) 690 ->setAttribute('data-row', $row_num) 691 ->addClass(ZBX_STYLE_BTN_ALT) 692 ) 693 ])) 694 ->addClass(ZBX_STYLE_OVERRIDES_LIST_ITEM); 695 } 696 697 /** 698 * Return template used by dynamic rows in CWidgetFieldGraphOverride field. 699 * 700 * @param CWidgetFieldGraphOverride $field 701 * @param string $form_name Form name in which override field is located. 702 * 703 * @return string 704 */ 705 public static function getGraphOverrideTemplate($field, $form_name) { 706 $value = CWidgetFieldGraphOverride::getDefaults(); 707 708 return self::getGraphOverrideLayout($field, $value, $form_name, '#{rowNum}')->toString(); 709 } 710 711 /** 712 * @param CWidgetFieldGraphOverride $field 713 * 714 * @return CList 715 */ 716 public static function getGraphOverride($field, $form_name) { 717 $list = (new CList())->addClass(ZBX_STYLE_OVERRIDES_LIST); 718 719 $values = $field->getValue(); 720 721 if (!$values) { 722 $values = []; 723 } 724 725 $i = 0; 726 727 foreach ($values as $override) { 728 $list->addItem(self::getGraphOverrideLayout($field, $override, $form_name, $i)); 729 730 $i++; 731 } 732 733 // Add 'Add' button under the list. 734 $list->addItem( 735 (new CDiv( 736 (new CButton('override_add', [(new CSpan())->addClass(ZBX_STYLE_PLUS_ICON), _('Add new override')])) 737 ->addClass(ZBX_STYLE_BTN_ALT) 738 ->setId('override-add') 739 )), 740 'overrides-foot' 741 ); 742 743 return $list; 744 } 745 746 /** 747 * Function returns array containing string values used as titles for override options. 748 * 749 * @return array 750 */ 751 private static function getGraphOverrideOptionNames() { 752 return [ 753 'width' => _('Width'), 754 'type' => _('Draw'), 755 'type'.SVG_GRAPH_TYPE_LINE => _('Line'), 756 'type'.SVG_GRAPH_TYPE_POINTS => _('Points'), 757 'type'.SVG_GRAPH_TYPE_STAIRCASE => _('Staircase'), 758 'type'.SVG_GRAPH_TYPE_BAR => _('Bar'), 759 'transparency' => _('Transparency'), 760 'fill' => _('Fill'), 761 'pointsize' => _('Point size'), 762 'missingdatafunc' => _('Missing data'), 763 'missingdatafunc'.SVG_GRAPH_MISSING_DATA_NONE => _('None'), 764 'missingdatafunc'.SVG_GRAPH_MISSING_DATA_CONNECTED => _x('Connected', 'missing data function'), 765 'missingdatafunc'.SVG_GRAPH_MISSING_DATA_TREAT_AS_ZERO => _x('Treat as 0', 'missing data function'), 766 'axisy' => _('Y-axis'), 767 'axisy'.GRAPH_YAXIS_SIDE_LEFT => _('Left'), 768 'axisy'.GRAPH_YAXIS_SIDE_RIGHT => _('Right'), 769 'timeshift' => _('Time shift') 770 ]; 771 } 772 773 /** 774 * Function returns array used to construct override field menu of available override options. 775 * 776 * @return array 777 */ 778 private static function getGraphOverrideMenu() { 779 return [ 780 'sections' => [ 781 [ 782 'name' => _('ADD OVERRIDE'), 783 'options' => [ 784 ['name' => _('Base colour'), 'callback' => 'addOverride', 'args' => ['color', '']], 785 786 ['name' => _('Width').'/0', 'callback' => 'addOverride', 'args' => ['width', 0]], 787 ['name' => _('Width').'/1', 'callback' => 'addOverride', 'args' => ['width', 1]], 788 ['name' => _('Width').'/2', 'callback' => 'addOverride', 'args' => ['width', 2]], 789 ['name' => _('Width').'/3', 'callback' => 'addOverride', 'args' => ['width', 3]], 790 ['name' => _('Width').'/4', 'callback' => 'addOverride', 'args' => ['width', 4]], 791 ['name' => _('Width').'/5', 'callback' => 'addOverride', 'args' => ['width', 5]], 792 ['name' => _('Width').'/6', 'callback' => 'addOverride', 'args' => ['width', 6]], 793 ['name' => _('Width').'/7', 'callback' => 'addOverride', 'args' => ['width', 7]], 794 ['name' => _('Width').'/8', 'callback' => 'addOverride', 'args' => ['width', 8]], 795 ['name' => _('Width').'/9', 'callback' => 'addOverride', 'args' => ['width', 9]], 796 ['name' => _('Width').'/10', 'callback' => 'addOverride', 'args' => ['width', 10]], 797 798 ['name' => _('Draw').'/'._('Line'), 'callback' => 'addOverride', 'args' => ['type', SVG_GRAPH_TYPE_LINE]], 799 ['name' => _('Draw').'/'._('Points'), 'callback' => 'addOverride', 'args' => ['type', SVG_GRAPH_TYPE_POINTS]], 800 ['name' => _('Draw').'/'._('Staircase'), 'callback' => 'addOverride', 'args' => ['type', SVG_GRAPH_TYPE_STAIRCASE]], 801 ['name' => _('Draw').'/'._('Bar'), 'callback' => 'addOverride', 'args' => ['type', SVG_GRAPH_TYPE_BAR]], 802 803 ['name' => _('Transparency').'/0', 'callback' => 'addOverride', 'args' => ['transparency', 0]], 804 ['name' => _('Transparency').'/1', 'callback' => 'addOverride', 'args' => ['transparency', 1]], 805 ['name' => _('Transparency').'/2', 'callback' => 'addOverride', 'args' => ['transparency', 2]], 806 ['name' => _('Transparency').'/3', 'callback' => 'addOverride', 'args' => ['transparency', 3]], 807 ['name' => _('Transparency').'/4', 'callback' => 'addOverride', 'args' => ['transparency', 4]], 808 ['name' => _('Transparency').'/5', 'callback' => 'addOverride', 'args' => ['transparency', 5]], 809 ['name' => _('Transparency').'/6', 'callback' => 'addOverride', 'args' => ['transparency', 6]], 810 ['name' => _('Transparency').'/7', 'callback' => 'addOverride', 'args' => ['transparency', 7]], 811 ['name' => _('Transparency').'/8', 'callback' => 'addOverride', 'args' => ['transparency', 8]], 812 ['name' => _('Transparency').'/9', 'callback' => 'addOverride', 'args' => ['transparency', 9]], 813 ['name' => _('Transparency').'/10', 'callback' => 'addOverride', 'args' => ['transparency', 10]], 814 815 ['name' => _('Fill').'/0', 'callback' => 'addOverride', 'args' => ['fill', 0]], 816 ['name' => _('Fill').'/1', 'callback' => 'addOverride', 'args' => ['fill', 1]], 817 ['name' => _('Fill').'/2', 'callback' => 'addOverride', 'args' => ['fill', 2]], 818 ['name' => _('Fill').'/3', 'callback' => 'addOverride', 'args' => ['fill', 3]], 819 ['name' => _('Fill').'/4', 'callback' => 'addOverride', 'args' => ['fill', 4]], 820 ['name' => _('Fill').'/5', 'callback' => 'addOverride', 'args' => ['fill', 5]], 821 ['name' => _('Fill').'/6', 'callback' => 'addOverride', 'args' => ['fill', 6]], 822 ['name' => _('Fill').'/7', 'callback' => 'addOverride', 'args' => ['fill', 7]], 823 ['name' => _('Fill').'/8', 'callback' => 'addOverride', 'args' => ['fill', 8]], 824 ['name' => _('Fill').'/9', 'callback' => 'addOverride', 'args' => ['fill', 9]], 825 ['name' => _('Fill').'/10', 'callback' => 'addOverride', 'args' => ['fill', 10]], 826 827 ['name' => _('Point size').'/1', 'callback' => 'addOverride', 'args' => ['pointsize', 1]], 828 ['name' => _('Point size').'/2', 'callback' => 'addOverride', 'args' => ['pointsize', 2]], 829 ['name' => _('Point size').'/3', 'callback' => 'addOverride', 'args' => ['pointsize', 3]], 830 ['name' => _('Point size').'/4', 'callback' => 'addOverride', 'args' => ['pointsize', 4]], 831 ['name' => _('Point size').'/5', 'callback' => 'addOverride', 'args' => ['pointsize', 5]], 832 ['name' => _('Point size').'/6', 'callback' => 'addOverride', 'args' => ['pointsize', 6]], 833 ['name' => _('Point size').'/7', 'callback' => 'addOverride', 'args' => ['pointsize', 7]], 834 ['name' => _('Point size').'/8', 'callback' => 'addOverride', 'args' => ['pointsize', 8]], 835 ['name' => _('Point size').'/9', 'callback' => 'addOverride', 'args' => ['pointsize', 9]], 836 ['name' => _('Point size').'/10', 'callback' => 'addOverride', 'args' => ['pointsize', 10]], 837 838 ['name' => _('Missing data').'/'._('None'), 'callback' => 'addOverride', 'args' => ['missingdatafunc', SVG_GRAPH_MISSING_DATA_NONE]], 839 ['name' => _('Missing data').'/'._x('Connected', 'missing data function'), 'callback' => 'addOverride', 'args' => ['missingdatafunc', SVG_GRAPH_MISSING_DATA_CONNECTED]], 840 ['name' => _('Missing data').'/'._x('Treat as 0', 'missing data function'), 'callback' => 'addOverride', 'args' => ['missingdatafunc', SVG_GRAPH_MISSING_DATA_TREAT_AS_ZERO]], 841 842 ['name' => _('Y-axis').'/'._('Left'), 'callback' => 'addOverride', 'args' => ['axisy', GRAPH_YAXIS_SIDE_LEFT]], 843 ['name' => _('Y-axis').'/'._('Right'), 'callback' => 'addOverride', 'args' => ['axisy', GRAPH_YAXIS_SIDE_RIGHT]], 844 845 ['name' => _('Time shift'), 'callback' => 'addOverride', 'args' => ['timeshift']] 846 ] 847 ] 848 ] 849 ]; 850 } 851 852 /** 853 * Return javascript necessary to initialize CWidgetFieldGraphOverride field. 854 * 855 * @param CWidgetFieldGraphOverride $field 856 * 857 * @return string 858 */ 859 public static function getGraphOverrideJavascript($field) { 860 $scripts = [ 861 // Define it as function to avoid redundancy. 862 'function initializeOverrides() {'. 863 'jQuery("#overrides .'.ZBX_STYLE_OVERRIDES_OPTIONS_LIST.'").overrides({'. 864 'add: ".'.ZBX_STYLE_BTN_ALT.'",'. 865 'options: "input[type=hidden]",'. 866 'captions: '.json_encode(self::getGraphOverrideOptionNames()).','. 867 'makeName: function(option, row_id) {'. 868 'return "'.$field->getName().'[" + row_id + "][" + option + "]";'. 869 '},'. 870 'makeOption: function(name) {'. 871 'return name.match('. 872 '/.*\[('.implode('|', CWidgetFieldGraphOverride::getOverrideOptions()).')\]/'. 873 ')[1];'. 874 '},'. 875 'override: ".'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'",'. 876 'overridesList: ".'.ZBX_STYLE_OVERRIDES_LIST.'",'. 877 'onUpdate: onGraphConfigChange,'. 878 'menu: '.json_encode(self::getGraphOverrideMenu()). 879 '});'. 880 '}', 881 882 // Initialize dynamicRows. 883 'jQuery("#overrides")'. 884 '.dynamicRows({'. 885 'template: "#overrides-row",'. 886 'beforeRow: ".overrides-foot",'. 887 'remove: ".'.ZBX_STYLE_REMOVE_BTN.'",'. 888 'add: "#override-add",'. 889 'row: ".'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'"'. 890 '})'. 891 '.bind("afteradd.dynamicRows", function(event, options) {'. 892 'var container = jQuery(".overlay-dialogue-body");'. 893 'container.scrollTop(Math.max(container.scrollTop(), 894 jQuery("#widget-dialogue-form")[0].scrollHeight - container.height() 895 ));'. 896 897 'jQuery(".multiselect", jQuery("#overrides")).each(function() {'. 898 'jQuery(this).multiSelect(jQuery(this).data("params"));'. 899 '});'. 900 'updateVariableOrder(jQuery("#overrides"), ".'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'", "or");'. 901 'onGraphConfigChange();'. 902 '})'. 903 '.bind("afterremove.dynamicRows", function(event, options) {'. 904 'updateVariableOrder(jQuery("#overrides"), ".'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'", "or");'. 905 'onGraphConfigChange();'. 906 '})'. 907 '.bind("tableupdate.dynamicRows", function(event, options) {'. 908 'updateVariableOrder(jQuery("#overrides"), ".'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'", "or");'. 909 'initializeOverrides();'. 910 'if (jQuery("#overrides .'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'").length > 1) {'. 911 'jQuery("#overrides .drag-icon").removeClass("disabled");'. 912 'jQuery("#overrides").sortable("enable");'. 913 '}'. 914 'else {'. 915 'jQuery("#overrides .drag-icon").addClass("disabled");'. 916 'jQuery("#overrides").sortable("disable");'. 917 '}'. 918 '});', 919 920 // Initialize overrides UI control. 921 'initializeOverrides();', 922 923 // Initialize override pattern-selectors. 924 'jQuery(".multiselect", jQuery("#overrides")).each(function() {'. 925 'jQuery(this).multiSelect(jQuery(this).data("params"));'. 926 '});', 927 928 // Make overrides sortable. 929 'if (jQuery("#overrides .'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'").length < 2) {'. 930 'jQuery("#overrides .drag-icon").addClass("disabled");'. 931 '}'. 932 'jQuery("#overrides").sortable({'. 933 'items: ".'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'",'. 934 'containment: "parent",'. 935 'handle: ".drag-icon",'. 936 'tolerance: "pointer",'. 937 'scroll: false,'. 938 'cursor: "grabbing",'. 939 'opacity: 0.6,'. 940 'axis: "y",'. 941 'disabled: function() {'. 942 'return jQuery("#overrides .'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'").length < 2;'. 943 '}(),'. 944 'start: function() {'. // Workaround to fix wrong scrolling at initial sort. 945 'jQuery(this).sortable("refreshPositions");'. 946 '},'. 947 'stop: onGraphConfigChange,'. 948 'update: function() {'. 949 'updateVariableOrder(jQuery("#overrides"), ".'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'", "or");'. 950 '}'. 951 '});' 952 ]; 953 954 return implode ('', $scripts); 955 } 956 957 /** 958 * Function returns array containing HTML objects filled with given values. Used to generate HTML row in widget 959 * data set field. 960 * 961 * @param string $field_name 962 * @param array $value Values to fill in particular data set row. See self::setValue() for detailed 963 * description. 964 * @param string $form_name Name of form in which data set fields resides. 965 * @param int|string $row_num Unique data set numeric identifier. Used to make unique field names. 966 * @param bool $is_opened Either accordion row is made opened or closed. 967 * 968 * @return CListItem 969 */ 970 private static function getGraphDataSetLayout($field_name, array $value, $form_name, $row_num, $is_opened) { 971 return (new CListItem([ 972 // Accordion head - data set selection fields and tools. 973 (new CDiv([ 974 (new CDiv()) 975 ->addClass(ZBX_STYLE_DRAG_ICON) 976 ->addStyle('position: absolute; margin-left: -25px;'), 977 (new CDiv([ 978 (new CDiv([ 979 (new CButton()) 980 ->addClass(ZBX_STYLE_COLOR_PREVIEW_BOX) 981 ->addStyle('background-color: #'.$value['color'].';') 982 ->setAttribute('title', $is_opened ? _('Collapse') : _('Expand')) 983 ->removeId(), 984 (new CPatternSelect([ 985 'name' => $field_name.'['.$row_num.'][hosts][]', 986 'object_name' => 'hosts', 987 'data' => $value['hosts'], 988 'placeholder' => _('host pattern'), 989 'popup' => [ 990 'parameters' => [ 991 'srctbl' => 'hosts', 992 'srcfld1' => 'host', 993 'dstfrm' => $form_name, 994 'dstfld1' => zbx_formatDomId($field_name.'['.$row_num.'][hosts][]') 995 ] 996 ], 997 'add_post_js' => false 998 ]))->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH) 999 ]))->addClass(ZBX_STYLE_COLUMN_50), 1000 (new CDiv( 1001 (new CPatternSelect([ 1002 'name' => $field_name.'['.$row_num.'][items][]', 1003 'object_name' => 'items', 1004 'data' => $value['items'], 1005 'placeholder' => _('item pattern'), 1006 'popup' => [ 1007 'parameters' => [ 1008 'srctbl' => 'items', 1009 'srcfld1' => 'name', 1010 'real_hosts' => 1, 1011 'numeric' => 1, 1012 'webitems' => 1, 1013 'orig_names' => 1, 1014 'dstfrm' => $form_name, 1015 'dstfld1' => zbx_formatDomId($field_name.'['.$row_num.'][items][]') 1016 ] 1017 ], 1018 'add_post_js' => false 1019 ]))->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH) 1020 ))->addClass(ZBX_STYLE_COLUMN_50) 1021 ])) 1022 ->addClass(ZBX_STYLE_COLUMNS) 1023 ->addClass(ZBX_STYLE_COLUMNS_NOWRAP) 1024 ->addClass(ZBX_STYLE_COLUMN_95), 1025 (new CDiv([ 1026 (new CButton()) 1027 ->setAttribute('title', _('Delete')) 1028 ->addClass(ZBX_STYLE_REMOVE_BTN) 1029 ->removeId() 1030 ]))->addClass(ZBX_STYLE_COLUMN_5) 1031 ])) 1032 ->addClass(ZBX_STYLE_LIST_ACCORDION_ITEM_HEAD) 1033 ->addClass(ZBX_STYLE_COLUMNS), 1034 1035 // Accordion body - data set configuration options. 1036 (new CDiv( 1037 (new CDiv([ 1038 // Left column fields. 1039 (new CDiv( 1040 (new CFormList()) 1041 ->addRow(_('Base colour'), 1042 (new CColor($field_name.'['.$row_num.'][color]', $value['color'])) 1043 ->appendColorPickerJs(false) 1044 ) 1045 ->addRow(_('Draw'), 1046 (new CRadioButtonList($field_name.'['.$row_num.'][type]', (int) $value['type'])) 1047 ->addValue(_('Line'), SVG_GRAPH_TYPE_LINE) 1048 ->addValue(_('Points'), SVG_GRAPH_TYPE_POINTS) 1049 ->addValue(_('Staircase'), SVG_GRAPH_TYPE_STAIRCASE) 1050 ->addValue(_('Bar'), SVG_GRAPH_TYPE_BAR) 1051 ->onChange('changeDataSetDrawType(this)') 1052 ->setModern(true) 1053 ) 1054 ->addRow(_('Width'), 1055 (new CRangeControl($field_name.'['.$row_num.'][width]', (int) $value['width'])) 1056 ->setEnabled(!in_array($value['type'], [SVG_GRAPH_TYPE_POINTS, SVG_GRAPH_TYPE_BAR])) 1057 ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH) 1058 ->setStep(1) 1059 ->setMin(0) 1060 ->setMax(10) 1061 ) 1062 ->addRow(_('Point size'), 1063 (new CRangeControl($field_name.'['.$row_num.'][pointsize]', (int) $value['pointsize'])) 1064 ->setEnabled($value['type'] == SVG_GRAPH_TYPE_POINTS) 1065 ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH) 1066 ->setStep(1) 1067 ->setMin(1) 1068 ->setMax(10) 1069 ) 1070 ->addRow(_('Transparency'), 1071 (new CRangeControl($field_name.'['.$row_num.'][transparency]', 1072 (int) $value['transparency']) 1073 ) 1074 ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH) 1075 ->setStep(1) 1076 ->setMin(0) 1077 ->setMax(10) 1078 ) 1079 ->addRow(_('Fill'), 1080 (new CRangeControl($field_name.'['.$row_num.'][fill]', (int) $value['fill'])) 1081 ->setEnabled(!in_array($value['type'], [SVG_GRAPH_TYPE_POINTS, SVG_GRAPH_TYPE_BAR])) 1082 ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH) 1083 ->setStep(1) 1084 ->setMin(0) 1085 ->setMax(10) 1086 ) 1087 ) 1088 ) 1089 ->addClass(ZBX_STYLE_COLUMN_50), 1090 1091 // Right column fields. 1092 (new CDiv( 1093 (new CFormList()) 1094 ->addRow(_('Missing data'), 1095 (new CRadioButtonList($field_name.'['.$row_num.'][missingdatafunc]', 1096 (int) $value['missingdatafunc']) 1097 ) 1098 ->addValue(_('None'), SVG_GRAPH_MISSING_DATA_NONE) 1099 ->addValue(_x('Connected', 'missing data function'), 1100 SVG_GRAPH_MISSING_DATA_CONNECTED 1101 ) 1102 ->addValue(_x('Treat as 0', 'missing data function'), 1103 SVG_GRAPH_MISSING_DATA_TREAT_AS_ZERO 1104 ) 1105 ->setEnabled(!in_array($value['type'], [SVG_GRAPH_TYPE_POINTS, SVG_GRAPH_TYPE_BAR])) 1106 ->setModern(true) 1107 ) 1108 ->addRow(_('Y-axis'), 1109 (new CRadioButtonList($field_name.'['.$row_num.'][axisy]', (int) $value['axisy'])) 1110 ->addValue(_('Left'), GRAPH_YAXIS_SIDE_LEFT) 1111 ->addValue(_('Right'), GRAPH_YAXIS_SIDE_RIGHT) 1112 ->setModern(true) 1113 ) 1114 ->addRow(_('Time shift'), 1115 (new CTextBox($field_name.'['.$row_num.'][timeshift]', $value['timeshift'])) 1116 ->setAttribute('placeholder', _('none')) 1117 ->setWidth(ZBX_TEXTAREA_TINY_WIDTH) 1118 ) 1119 ->addRow( 1120 new CLabel(_('Aggregation function'), 1121 'label-'.$field_name.'_'.$row_num.'_aggregate_function' 1122 ), 1123 (new CSelect($field_name.'['.$row_num.'][aggregate_function]')) 1124 ->setId($field_name.'_'.$row_num.'_aggregate_function') 1125 ->setFocusableElementId('label-'.$field_name.'_'.$row_num.'_aggregate_function') 1126 ->setValue((int) $value['aggregate_function']) 1127 ->addOptions(CSelect::createOptionsFromArray([ 1128 GRAPH_AGGREGATE_NONE => graph_item_aggr_fnc2str(GRAPH_AGGREGATE_NONE), 1129 GRAPH_AGGREGATE_MIN => graph_item_aggr_fnc2str(GRAPH_AGGREGATE_MIN), 1130 GRAPH_AGGREGATE_MAX => graph_item_aggr_fnc2str(GRAPH_AGGREGATE_MAX), 1131 GRAPH_AGGREGATE_AVG => graph_item_aggr_fnc2str(GRAPH_AGGREGATE_AVG), 1132 GRAPH_AGGREGATE_COUNT => graph_item_aggr_fnc2str(GRAPH_AGGREGATE_COUNT), 1133 GRAPH_AGGREGATE_SUM => graph_item_aggr_fnc2str(GRAPH_AGGREGATE_SUM), 1134 GRAPH_AGGREGATE_FIRST => graph_item_aggr_fnc2str(GRAPH_AGGREGATE_FIRST), 1135 GRAPH_AGGREGATE_LAST => graph_item_aggr_fnc2str(GRAPH_AGGREGATE_LAST) 1136 ])) 1137 ->setWidth(ZBX_TEXTAREA_TINY_WIDTH) 1138 ) 1139 ->addRow(_('Aggregation interval'), 1140 (new CTextBox( 1141 $field_name.'['.$row_num.'][aggregate_interval]', 1142 $value['aggregate_interval'] 1143 )) 1144 ->setEnabled($value['aggregate_function'] != GRAPH_AGGREGATE_NONE) 1145 ->setAttribute('placeholder', GRAPH_AGGREGATE_DEFAULT_INTERVAL) 1146 ->setWidth(ZBX_TEXTAREA_TINY_WIDTH) 1147 ) 1148 ->addRow(_('Aggregate'), 1149 (new CRadioButtonList( 1150 $field_name.'['.$row_num.'][aggregate_grouping]', 1151 (int) $value['aggregate_grouping']) 1152 ) 1153 ->addValue(_('Each item'), GRAPH_AGGREGATE_BY_ITEM) 1154 ->addValue(_('Data set'), GRAPH_AGGREGATE_BY_DATASET) 1155 ->setEnabled($value['aggregate_function'] != GRAPH_AGGREGATE_NONE) 1156 ->setModern(true) 1157 ) 1158 )) 1159 ->addClass(ZBX_STYLE_COLUMN_50) 1160 ])) 1161 ->addClass(ZBX_STYLE_COLUMNS) 1162 ->addClass(ZBX_STYLE_COLUMNS_NOWRAP) 1163 ->addClass(ZBX_STYLE_COLUMN_95) 1164 )) 1165 ->addClass(ZBX_STYLE_LIST_ACCORDION_ITEM_BODY) 1166 ->addClass(ZBX_STYLE_COLUMNS) 1167 ])) 1168 ->addClass(ZBX_STYLE_LIST_ACCORDION_ITEM) 1169 ->addClass($is_opened ? ZBX_STYLE_LIST_ACCORDION_ITEM_OPENED : ZBX_STYLE_LIST_ACCORDION_ITEM_CLOSED); 1170 } 1171 1172 /** 1173 * Return template used by dynamic rows in CWidgetFieldGraphDataSet field. 1174 * 1175 * @param CWidgetFieldGraphDataSet $field 1176 * @param string $form_name Form name in which data set field resides. 1177 * 1178 * @return string 1179 */ 1180 public static function getGraphDataSetTemplate($field, $form_name) { 1181 $value = ['color' => '#{color}'] + CWidgetFieldGraphDataSet::getDefaults(); 1182 1183 return self::getGraphDataSetLayout($field->getName(), $value, $form_name, '#{rowNum}', true)->toString(); 1184 } 1185 1186 /** 1187 * @param CWidgetFieldGraphDataSet $field 1188 * 1189 * @return CList 1190 */ 1191 public static function getGraphDataSet($field, $form_name) { 1192 $list = (new CList()) 1193 ->addClass(ZBX_STYLE_LIST_VERTICAL_ACCORDION) 1194 ->setId('data_sets'); 1195 1196 $values = $field->getValue(); 1197 1198 if (!$values) { 1199 $values[] = CWidgetFieldGraphDataSet::getDefaults(); 1200 } 1201 1202 foreach ($values as $i => $value) { 1203 $list->addItem(self::getGraphDataSetLayout($field->getName(), $value, $form_name, $i, $i == 0)); 1204 } 1205 1206 // Add 'Add' button under accordion. 1207 $list->addItem( 1208 (new CDiv( 1209 (new CButton('data_sets_add', [(new CSpan())->addClass(ZBX_STYLE_PLUS_ICON), _('Add new data set')])) 1210 ->addClass(ZBX_STYLE_BTN_ALT) 1211 ->setId('dataset-add') 1212 )), 1213 ZBX_STYLE_LIST_ACCORDION_FOOT 1214 ); 1215 1216 return $list; 1217 } 1218 1219 /** 1220 * Return javascript necessary to initialize CWidgetFieldGraphDataSet field. 1221 * 1222 * @return string 1223 */ 1224 public static function getGraphDataSetJavascript() { 1225 $scripts = [ 1226 'function changeDataSetDrawType(obj) {'. 1227 'var row_num = obj.id.replace("ds_", "").replace("_type", "");'. 1228 'switch (jQuery(":checked", jQuery(obj)).val()) {'. 1229 'case "'.SVG_GRAPH_TYPE_POINTS.'":'. 1230 'jQuery("#ds_" + row_num + "_width").rangeControl("disable");'. 1231 'jQuery("#ds_" + row_num + "_pointsize").rangeControl("enable");'. 1232 'jQuery("#ds_" + row_num + "_transparency").rangeControl("enable");'. 1233 'jQuery("#ds_" + row_num + "_fill").rangeControl("disable");'. 1234 'jQuery("#ds_" + row_num + "_missingdatafunc_0").prop("disabled", true);'. 1235 'jQuery("#ds_" + row_num + "_missingdatafunc_1").prop("disabled", true);'. 1236 'jQuery("#ds_" + row_num + "_missingdatafunc_2").prop("disabled", true);'. 1237 'break;'. 1238 'case "'.SVG_GRAPH_TYPE_BAR.'":'. 1239 'jQuery("#ds_" + row_num + "_width").rangeControl("disable");'. 1240 'jQuery("#ds_" + row_num + "_pointsize").rangeControl("disable");'. 1241 'jQuery("#ds_" + row_num + "_transparency").rangeControl("enable");'. 1242 'jQuery("#ds_" + row_num + "_fill").rangeControl("disable");'. 1243 'jQuery("#ds_" + row_num + "_missingdatafunc_0").prop("disabled", true);'. 1244 'jQuery("#ds_" + row_num + "_missingdatafunc_1").prop("disabled", true);'. 1245 'jQuery("#ds_" + row_num + "_missingdatafunc_2").prop("disabled", true);'. 1246 'break;'. 1247 'default:'. 1248 'jQuery("#ds_" + row_num + "_width").rangeControl("enable");'. 1249 'jQuery("#ds_" + row_num + "_pointsize").rangeControl("disable");'. 1250 'jQuery("#ds_" + row_num + "_transparency").rangeControl("enable");'. 1251 'jQuery("#ds_" + row_num + "_fill").rangeControl("enable");'. 1252 'jQuery("#ds_" + row_num + "_missingdatafunc_0").prop("disabled", false);'. 1253 'jQuery("#ds_" + row_num + "_missingdatafunc_1").prop("disabled", false);'. 1254 'jQuery("#ds_" + row_num + "_missingdatafunc_2").prop("disabled", false);'. 1255 'break;'. 1256 '}'. 1257 '};', 1258 1259 'function changeDataSetAggregateFunction(obj) {'. 1260 'var row_num = obj.id.replace("ds_", "").replace("_aggregate_function", "");'. 1261 'var no_aggregation = (jQuery(obj).val() == '.GRAPH_AGGREGATE_NONE.');'. 1262 'jQuery("#ds_" + row_num + "_aggregate_interval").prop("disabled", no_aggregation);'. 1263 'jQuery("#ds_" + row_num + "_aggregate_grouping_0").prop("disabled", no_aggregation);'. 1264 'jQuery("#ds_" + row_num + "_aggregate_grouping_1").prop("disabled", no_aggregation);'. 1265 '};', 1266 1267 // Initialize dynamic rows. 1268 'jQuery("#data_sets")'. 1269 '.dynamicRows({'. 1270 'template: "#dataset-row",'. 1271 'beforeRow: ".'.ZBX_STYLE_LIST_ACCORDION_FOOT.'",'. 1272 'remove: ".'.ZBX_STYLE_REMOVE_BTN.'",'. 1273 'add: "#dataset-add",'. 1274 'row: ".'.ZBX_STYLE_LIST_ACCORDION_ITEM.'",'. 1275 'dataCallback: function(data) {'. 1276 'data.color = function(num) {'. 1277 'var palette = '.CWidgetFieldGraphDataSet::DEFAULT_COLOR_PALETTE.';'. 1278 'return palette[num % palette.length];'. 1279 '} (data.rowNum);'. 1280 'return data;'. 1281 '}'. 1282 '})'. 1283 '.bind("beforeadd.dynamicRows", function(event, options) {'. 1284 'jQuery("#data_sets").zbx_vertical_accordion("collapseAll");'. 1285 '})'. 1286 '.bind("afteradd.dynamicRows", function(event, options) {'. 1287 'var container = jQuery(".overlay-dialogue-body");'. 1288 'container.scrollTop(Math.max(container.scrollTop(), 1289 jQuery("#widget-dialogue-form")[0].scrollHeight - container.height() 1290 ));'. 1291 1292 'jQuery(".input-color-picker input").colorpicker({onUpdate: function(color) {'. 1293 'var ds = jQuery(this).closest(".'.ZBX_STYLE_LIST_ACCORDION_ITEM.'");'. 1294 'jQuery(".'.ZBX_STYLE_COLOR_PREVIEW_BOX.'", ds).css("background-color", "#"+color);'. 1295 '}, appendTo: ".overlay-dialogue-body"});'. 1296 1297 'jQuery(".multiselect", jQuery("#data_sets")).each(function() {'. 1298 'jQuery(this).multiSelect(jQuery(this).data("params"));'. 1299 '});'. 1300 'updateVariableOrder(jQuery("#data_sets"), ".'.ZBX_STYLE_LIST_ACCORDION_ITEM.'", "ds");'. 1301 'onGraphConfigChange();'. 1302 '})'. 1303 '.bind("afterremove.dynamicRows", function(event, options) {'. 1304 'updateVariableOrder(jQuery("#data_sets"), ".'.ZBX_STYLE_LIST_ACCORDION_ITEM.'", "ds");'. 1305 'onGraphConfigChange();'. 1306 '})'. 1307 '.bind("tableupdate.dynamicRows", function(event, options) {'. 1308 'updateVariableOrder(jQuery("#data_sets"), ".'.ZBX_STYLE_LIST_ACCORDION_ITEM.'", "ds");'. 1309 'jQuery(".'.CRangeControl::ZBX_STYLE_CLASS.'[data-options]").rangeControl();'. 1310 'if (jQuery("#data_sets .'.ZBX_STYLE_LIST_ACCORDION_ITEM.'").length > 1) {'. 1311 'jQuery("#data_sets .drag-icon").removeClass("disabled");'. 1312 'jQuery("#data_sets").sortable("enable");'. 1313 '}'. 1314 'else {'. 1315 'jQuery("#data_sets .drag-icon").addClass("disabled");'. 1316 'jQuery("#data_sets").sortable("disable");'. 1317 '}'. 1318 '});', 1319 1320 // Initialize vertical accordion. 1321 'jQuery("#data_sets")'. 1322 '.on("focus", ".'.CMultiSelect::ZBX_STYLE_CLASS.' input.input", function() {'. 1323 'jQuery("#data_sets").zbx_vertical_accordion("expandNth",'. 1324 'jQuery(this).closest(".'.ZBX_STYLE_LIST_ACCORDION_ITEM.'").index());'. 1325 '})'. 1326 '.on("collapse", function(event, data) {'. 1327 'jQuery("textarea, .multiselect", data.section).scrollTop(0);'. 1328 'jQuery(window).trigger("resize");'. 1329 '})'. 1330 '.on("expand", function() {'. 1331 'jQuery(window).trigger("resize");'. 1332 '})'. 1333 '.zbx_vertical_accordion({handler: ".'.ZBX_STYLE_COLOR_PREVIEW_BOX.'"});', 1334 1335 // Initialize rangeControl UI elements. 1336 'jQuery(".'.CRangeControl::ZBX_STYLE_CLASS.'", jQuery("#data_sets")).rangeControl();', 1337 1338 // Expand dataset when click in pattern fields. 1339 'jQuery("#data_sets").on("click", "'.implode(', ', [ 1340 '.'.ZBX_STYLE_LIST_ACCORDION_ITEM_CLOSED.' .'.CPatternSelect::ZBX_STYLE_CLASS, 1341 '.'.ZBX_STYLE_LIST_ACCORDION_ITEM_CLOSED.' .'.ZBX_STYLE_BTN_GREY 1342 ]).'", function(event) {'. 1343 'var index = jQuery(this).closest(".'.ZBX_STYLE_LIST_ACCORDION_ITEM.'").index();'. 1344 'jQuery("#data_sets").zbx_vertical_accordion("expandNth", index);'. 1345 'jQuery(event.currentTarget).find("input.input").focus();'. 1346 '});', 1347 1348 // Initialize pattern fields. 1349 'jQuery(".multiselect", jQuery("#data_sets")).each(function() {'. 1350 'jQuery(this).multiSelect(jQuery(this).data("params"));'. 1351 '});', 1352 1353 // Initialize color-picker UI elements. 1354 'jQuery(".input-color-picker input").colorpicker({onUpdate: function(color){'. 1355 'var ds = jQuery(this).closest(".'.ZBX_STYLE_LIST_ACCORDION_ITEM.'");'. 1356 'jQuery(".'.ZBX_STYLE_COLOR_PREVIEW_BOX.'", ds).css("background-color", "#"+color);'. 1357 '}, appendTo: ".overlay-dialogue-body"});', 1358 1359 // Initialize sortability. 1360 'if (jQuery("#data_sets .'.ZBX_STYLE_LIST_ACCORDION_ITEM.'").length < 2) {'. 1361 'jQuery("#data_sets .drag-icon").addClass("disabled");'. 1362 '}'. 1363 'jQuery("#data_sets").sortable({'. 1364 'items: ".'.ZBX_STYLE_LIST_ACCORDION_ITEM.'",'. 1365 'containment: "parent",'. 1366 'handle: ".drag-icon",'. 1367 'tolerance: "pointer",'. 1368 'scroll: false,'. 1369 'cursor: "grabbing",'. 1370 'opacity: 0.6,'. 1371 'axis: "y",'. 1372 'disabled: function() {'. 1373 'return jQuery("#data_sets .'.ZBX_STYLE_LIST_ACCORDION_ITEM.'").length < 2;'. 1374 '}(),'. 1375 'start: function() {'. // Workaround to fix wrong scrolling at initial sort. 1376 'jQuery(this).sortable("refreshPositions");'. 1377 '},'. 1378 'stop: onGraphConfigChange,'. 1379 'update: function() {'. 1380 'updateVariableOrder(jQuery("#data_sets"), ".'.ZBX_STYLE_LIST_ACCORDION_ITEM.'", "ds");'. 1381 '}'. 1382 '});'. 1383 '$(".overlay-dialogue-body").on("change", "z-select[id$=\"aggregate_function\"]", (e) => {'. 1384 'changeDataSetAggregateFunction(e.target);'. 1385 '});' 1386 ]; 1387 1388 return implode ('', $scripts); 1389 } 1390 1391 /** 1392 * @param CWidgetField $field 1393 * 1394 * @return int 1395 */ 1396 public static function isAriaRequired($field) { 1397 return ($field->getFlags() & CWidgetField::FLAG_LABEL_ASTERISK); 1398 } 1399} 1400